I am currently working on a javascript plugin that will be able to take over a html document loading screen. The plugin is very basic at the moment, but more features are to be added soon.
There's a problem though, I cannot contsruct the element and place it in the body of the html document.
I've run out of options, and really need my plugin to construct and place itself in the body, before I can go on styling the plugin.
Here is my javascript code:
(function() {
// Define the loading constructor
this.loader = function() {
// Create global element references
this.loader = null;
this.overlaycolor = null;
// Define option defaults
var defaults = {
loaderclassName: 'loader',
textclassName: 'loadertext',
Content: "WE'RE LOADING SOME STUFF",
overlayColor: '#ffffff',
Width: 100,
Height: 100,
Text: True
};
// Create options by extending defaults with the passed in arugments
if (arguments[0] && typeof arguments[0] === "object") {
this.options = extendDefaults(defaults, arguments[0]);
}
var myLoader = new loader();
myLoader.prototype.open();
};
// Public Methods
loader.prototype.open = function() {
// Build out our loader
buildOut.call(this);
};
// Private Methods
function buildOut() {
var content, contentHolder, docFrag;
/*
* If content is an HTML string, append the HTML string.
* If content is a domNode, append its content.
*/
if (typeof this.options.content === "string") {
content = this.options.content;
} else {
content = this.options.content.innerHTML;
}
// Create a DocumentFragment to build with
docFrag = document.createDocumentFragment();
// Create loading element
this.loader = document.createElement("div");
this.loader.className = "exentory-loader" + this.options.loaderclassName;
this.loader.style.Width = this.options.Width + "%";
this.loader.style.Height = this.options.Height + "%";
this.loader.css("background-color", this.options.overlayColor);
// If closeButton option is true, add a close button
/*if (this.options.closeButton === true) {
this.closeButton = document.createElement("button");
this.closeButton.className = "scotch-close close-button";
this.closeButton.innerHTML = "×";
this.loader.appendChild(this.closeButton);
}*/
// If text is true, add textbox with content
if (this.options.Text === true) {
this.textbox = document.createElement("p");
this.textbox.className = "exentory-text " + this.options.textclassName;
this.textbox.append(this.options.Content);
docFrag.appendChild(this.loader);
}
// Append loader to DocumentFragment
docFrag.appendChild(this.loader);
// Append DocumentFragment to body
document.body.appendChild(docFrag);
}
// Utility method to extend defaults with user options
function extendDefaults(source, properties) {
var property;
for (property in properties) {
if (properties.hasOwnProperty(property)) {
source[property] = properties[property];
}
}
return source;
}
}());
I have no clue as to what is going wrong here. I've tried placing the myLoader.prototype.open() code averywhere in the file, but it does'nt construct.
I hope someone is able to help me.
Thanks in advance.
Try this:
this.loader = function() {
}
loader.prototype.open = function() {
alert("open")
}
And when you construct it:
var myLoader = new loader();
myLoader.open();
Working demo
Related
I am using Quill.js to have users create custom user designed pages off the main web page. I have a custom slider that I have written in javascript that will take images and rotate through them. I have the toolbar in quill setup to be able to click on the toolbar to setup the slider in a modal window.
When the user clicks to close the modal slider setup window, I'm trying to insert an edit button in the html editor where the cursor sits so the user can edit the information that was just entered or possible delete the button by removing all the information in the slider modal window.
I have written custom blots, followed all the examples I could find and nothing worked. I did get a custom html tag to show, but not a button. When I requested the html from quill when I setup the custom html tag in the editor, all I get is "" from quill.root.innerHTML none of the custom tag or the information in it even though I see it correctly in the editor.
I would like a button in the editor to make it easy to edit the slider data, as there could be more than one. I am not going to limit the number of sliders. It is up to the user to ruin their own page.
I will not be rendering the slider in the edit html window, just trying to display a button to click on. I do give the user a preview button on the modal window to view the slider if they so choose.
I also would like to store the setup information of the slider in a data tag in json format in the button and change that html button to a tag along with the json data when rendering the html in the browser window.
Can this be done in Quill.js?
I did this by using the import blots embed. I don't register as a button but clicking on the blot I do get a link to the embedded data and trigger the edit of the carousel data on that click.
var Quill;
const BlockEmbed = Quill.import("blots/block/embed");
const Link = Quill.import('formats/link');
class Carousel extends BlockEmbed {
static create(value) {
var self = this;
const node = super.create(value);
node.dataset.carousel = JSON.stringify(value);
value.file.frames.forEach(frame => {
const frameDiv = document.createElement("div");
frameDiv.innerText = frame.title;
frameDiv.className += "previewDiv";
node.appendChild(frameDiv);
});
node.onclick = function () {
if (Carousel.onClick) {
Carousel.onClick(self, node);
}
};
return node;
}
static value(domNode) {
return JSON.parse(domNode.dataset.carousel);
}
static onClick: any;
static blotName = "carousel";
static className = "ql-carousel";
static tagName = "DIV";
}
Quill.register("formats/carousel", Carousel);
I then include this as a script on the page where quill is editing the HTML
<script src="~/js/quilljs-carousel.js"></script>
And then this is the custom javascript to handle the events of the blot displayed in a custom modal dialog in a hidden div tag
<script>
function CarouselClicked(blot, node) {
editCarousel(JSON.parse(node.dataset.carousel), node);
}
var Carousel = Quill.import("formats/carousel");
Carousel.onClick = CarouselClicked;
function carouselToolbarClickHandler() {
createCarousel();
}
var Toolbar = Quill.import("modules/toolbar");
Toolbar.DEFAULTS.handlers.carousel = carouselToolbarClickHandler;
$(document).ready(function() {
var div = $("#editor-container");
var Image = Quill.import('formats/image');
Image.className = "img-fluid";
Quill.register(Image, true);
var quill = new Quill(div.get(0),
{
modules: {
syntax: true,
toolbar: '#toolbar-container'
},
theme: 'snow'
});
div.data('quill', quill);
// Only show the toolbar when quill is ready to go
$('#toolbar-container').css('display', 'block');
div.css('display', 'block');
$('#editor-loading').css('display', 'none');
// Override the quill Image handler and create our own.
quill.getModule("toolbar").addHandler("image", imageHandler);
});
function createCarousel() {
$("#carouselModalForm").removeClass("was-validated").get(0).reset();
$("#carouselModalList").empty();
$("#carouselModal")
.data("state", "unsaved")
.one("hidden.bs.modal",
function() {
var data = getCarouselData();
var carouselData = {};
carouselData['file'] = data;
carouselData['speed'] = $('#time').val();
carouselData['height'] = $('#height').val();
carouselData['width'] = $('#width').val();
var quill = $("#editor-container").data().quill;
var range = quill.getSelection(true);
if (range != null) {
quill.insertEmbed(
range.index,
"carousel",
carouselData,
Quill.sources.USER
);
quill.setSelection(range.index + 2, Quill.sources.USER);
}
})
.modal("show");
$('#title').get(0).outerText = "Create Carousel";
}
function editCarousel(value, node) {
$("#carouselModalForm").get(0).reset();
var elem = $("#carouselModalList").empty();
$.each(value.file.frames,
function(i, data) {
$("<option>").appendTo(elem).text(data.title).data("frame", data);
});
$('#time').val(value.speed);
$('#height').val(value.height);
$('#width').val(value.width);
var modal = $("#carouselModal");
modal.find("form").removeClass("was-validated");
modal
.data("state", "unsaved")
.one("hidden.bs.modal",
function() {
if ($("#carouselModal").data("state") !== "saved")
return;
var data = getCarouselData();
var carouselData = {};
carouselData['file'] = data;
carouselData['speed'] = $('#time').val();
carouselData['height'] = $('#height').val();
carouselData['width'] = $('#width').val();
var carousel = Quill.find(node, true);
carousel.replaceWith("carousel", carouselData);
})
.modal("show");
$('#title').get(0).outerText = "Edit Carousel";
}
function getCarouselData() {
var options = $("#carouselModalList").find("option");
var frames = $.map(options,
function(domNode) {
return $(domNode).data("frame");
});
return {
frames : frames
};
}
function getCarouselFrameFormData() {
// Get data from frame
function objectifyForm(formArray) { //serialize data function
var returnArray = {};
for (var i = 0; i < formArray.length; i++) {
returnArray[formArray[i]['name']] = formArray[i]['value'];
}
return returnArray;
}
return objectifyForm($("#carouselFrameModalForm").serializeArray());
}
$("#carouselModalNewFrame").click(function() {
$("#carouselFrameModalForm").removeClass("was-validated").get(0).reset();
$("#backgroundPreview").attr("src", "");
$("#mainPreview").attr("src", "");
$("#carouselFrameModal")
.data("state", "unsaved")
.one("hidden.bs.modal",
function() {
if ($("#carouselFrameModal").data("state") == "saved") {
var data = getCarouselFrameFormData();
$("<option>").appendTo($("#carouselModalList")).text(data.title).data("frame", data);
}
})
.modal("show");
// Fetch all the forms we want to apply custom Bootstrap validation styles to
});
$("#carouselModalEditFrame").click(function () {
var form = $("#carouselFrameModalForm");
form.removeClass("was-validated").get(0).reset();
var selected = $("#carouselModalList option:selected");
var frame = selected.data("frame");
$.each(Object.keys(frame),
function (i, e) {
$("input[name=" + e + "]", form).val(frame[e]);
});
$("#backgroundPreview").attr("src", frame.backgroundImg);
$("#mainPreview").attr("src", frame.mainImg);
$("#carouselFrameModal")
.data("state", "unsaved")
.one("hidden.bs.modal",
function() {
if ($("#carouselFrameModal").data("state") == "saved") {
var data = getCarouselFrameFormData();
selected.text(data.title).data("frame", data);
}
})
.modal("show");
});
$("#carouselModalRemoveFrame").click(function () {
$("#confirm-delete").modal("show");
});
$("#carouselFrameModalForm").find("input:file").change(function() {
var elem = $(this);
var target = elem.data("target");
var preview = elem.data("preview");
FiletoBase64(this, preview, target);
});
$("#carouselModalList").change(function() {
var selected = $("option:selected", this);
$("#carouselModalEditFrame,#carouselModalRemoveFrame").prop("disabled", !selected.length);
});
$("#carouselModalSave").click(function() {
// Validate frameset
var form = $("#carouselModalForm").get(0);
var select = $("#carouselModalList");
if (select.find("option").length == 0) {
select.get(0).setCustomValidity("Need at least one frame");
} else {
select.get(0).setCustomValidity("");
}
if (form.checkValidity()) {
$("#carouselModal").data("state", "saved");
$("#carouselModal").modal("hide");
}
else {
$("#carouselModalForm").addClass("was-validated");
}
});
$("#carouselFrameModalSave").click(function() {
// Validate frame
if ($("#carouselFrameModalForm").get(0).checkValidity()) {
$("#carouselFrameModal").data("state", "saved");
$("#carouselFrameModal").modal("hide");
}
else {
$("#carouselFrameModalForm").addClass("was-validated");
}
});
$('#confirm-delete-delete').click(function () {
$("#carouselModalList option:selected").remove();
$("#confirm-delete").modal("hide");
});
// #region Image FileExplorer Handler
function insertImageToEditor(url) {
var div = $("#editor-container");
var quill = div.data('quill');
var range = quill.getSelection();
quill.insertEmbed(range.index, 'image', url);
}
function CarouselMainimageHandler() {
$("#CarouselMainimageSelectFileExplorer").fileExplorer({
directoryListUrl: '#Url.Action("DirectoryList", "FileTree")',
fileListUrl: '#Url.Action("FileList", "FileTree")'
});
$("#CarouselMainimageSelectModal").modal("show");
}
$("#CarouselMainimageSelectModal").on("hide.bs.modal",
function() {
$("#CarouselMainimageSelectFileExplorer").fileExplorer("destroy");
});
$("#CarouselMainimageSelectSelectButton").click(function() {
var id = $("#CarouselMainimageSelectFileExplorer").fileExplorer("getSelectedFileId");
if (id == null) {
alert("Please select a file");
return;
}
var imageName = $("#CarouselMainimageSelectFileExplorer").fileExplorer("getSelectedFileName");
var imageLink = "#Url.Action("Render", "FileTree")?id=" + id;
$("#mainImageName").val(imageName);
$("#mainImageLink").val(imageLink);
$("#mainImage").attr("src",imageLink);
$("#mainImage").show();
$("#CarouselMainimageSelectModal").modal("hide");
});
function CarouselBackgroundimageHandler() {
$("#CarouselBackgroundimageSelectFileExplorer").fileExplorer({
directoryListUrl: '#Url.Action("DirectoryList", "FileTree")',
fileListUrl: '#Url.Action("FileList", "FileTree")'
});
$("#CarouselBackgroundimageSelectModal").modal("show");
}
$("#CarouselBackgroundimageSelectModal").on("hide.bs.modal",
function() {
$("#CarouselBackgroundimageSelectFileExplorer").fileExplorer("destroy");
});
$("#CarouselBackgroundimageSelectSelectButton").click(function() {
var id = $("#CarouselBackgroundimageSelectFileExplorer").fileExplorer("getSelectedFileId");
if (id == null) {
alert("Please select a file");
return;
}
var imageName = $("#CarouselBackgroundimageSelectFileExplorer").fileExplorer("getSelectedFileName");
var imageLink = "#Url.Action("Render", "FileTree")?id=" + id;
$("#backgroundImageName").val(imageName);
$("#backgroundImageLink").val(imageLink);
$("#backgroundImage").attr("src",imageLink);
$("#backgroundImage").show();
$("#CarouselBackgroundimageSelectModal").modal("hide");
});
function imageHandler() {
$("#imageSelectFileExplorer").fileExplorer({
directoryListUrl: '#Url.Action("DirectoryList", "FileTree")',
fileListUrl: '#Url.Action("FileList", "FileTree")'
});
$("#imageSelectModal").modal("show");
}
$("#imageSelectModal").on("hide.bs.modal",
function() {
$("#imageSelectFileExplorer").fileExplorer("destroy");
});
$("#imageSelectSelectButton").click(function() {
var id = $("#imageSelectFileExplorer").fileExplorer("getSelectedFileId");
if (id == null) {
alert("Please select a file");
return;
}
insertImageToEditor("#Url.Action("Render", "FileTree")?id=" + id);
$("#imageSelectModal").modal("hide");
});
// #endregion
function Save() {
var div = $("#editor-container");
var quill = div.data('quill');
$('#alertSave').show();
$.post({
url: '#Url.Action("Save")',
data: { BodyHtml: quill.root.innerHTML, locationId: #(Model.LocationId?.ToString() ?? "null") },
headers: {
'#Xsrf.HeaderName': '#Xsrf.RequestToken'
}
}).done(function() {
$('#alertSave').hide();
$('#alertSuccess').show();
setTimeout(function() { $('#alertSuccess').hide(); }, 5000);
}).fail(function(jqXhr, error) {
var alert = $('#alertFailure');
var text = $("#alertFailureText");
text.text(error.ErrorMessage);
alert.show();
});
}
function FiletoBase64(input, imgName, Base64TextName) {
var preview = document.getElementById(imgName);
var file = input.files[0];
var reader = new FileReader();
reader.addEventListener("load",
function() {
preview.src = reader.result;
document.getElementById(Base64TextName).value = reader.result;
},
false);
if (file) {
reader.readAsDataURL(file);
}
}
</script>
I am writing my first jQuery plugin which is a tree browser. It shall first show the top level elements and on click go deeper and show (depending on level) the children in a different way.
I got this up and running already. But now I want to implement a "back" functionality and for this I need to store an array of clicked elements for each instance of the tree browser (if multiple are on the page).
I know that I can put instance private variables with "this." in the plugin.
But if I assign an event handler of the onClick on a topic, how do I get this instance private variable? $(this) is referencing the clicked element at this moment.
Could please anyone give me an advise or a link to a tutorial how to get this done?
I only found tutorial for instance specific variables without event handlers involved.
Any help is appreciated.
Thanks in advance.
UPDATE: I cleaned out the huge code generation and kept the logical structure. This is my code:
(function ($) {
$.fn.myTreeBrowser = function (options) {
clickedElements = [];
var defaults = {
textColor: "#000",
backgroundColor: "#fff",
fontSize: "1em",
titleAttribute: "Title",
idAttribute: "Id",
parentIdAttribute: "ParentId",
levelAttribute: "Level",
treeData: {}
};
var opts = $.extend({}, $.fn.myTreeBrowser.defaults, options);
function getTreeData(id) {
if (opts.data) {
$.ajax(opts.data, { async: false, data: { Id: id } }).success(function (resultdata) {
opts.treeData = resultdata;
});
}
}
function onClick() {
var id = $(this).attr('data-id');
var parentContainer = getParentContainer($(this));
handleOnClick(parentContainer, id);
}
function handleOnClick(parentContainer, id) {
if (opts.onTopicClicked) {
opts.onTopicClicked(id);
}
clickedElements.push(id);
if (id) {
var clickedElement = $.grep(opts.treeData, function (n, i) { return n[opts.idAttribute] === id })[0];
switch (clickedElement[opts.levelAttribute]) {
case 1:
renderLevel2(parentContainer, clickedElement);
break;
case 3:
renderLevel3(parentContainer, clickedElement);
break;
default:
debug('invalid level element clicked');
}
} else {
renderTopLevel(parentContainer);
}
}
function getParentContainer(elem) {
return $(elem).parents('div.myBrowserContainer').parents()[0];
}
function onBackButtonClick() {
clickedElements.pop(); // remove actual element to get the one before
var lastClickedId = clickedElements.pop();
var parentContainer = getParentContainer($(this));
handleOnClick(parentContainer, lastClickedId);
}
function renderLevel2(parentContainer, selectedElement) {
$(parentContainer).html('');
var browsercontainer = $('<div>').addClass('myBrowserContainer').appendTo(parentContainer);
//... rendering the div ...
// for example like this with a onClick handler
var div = $('<div>').attr('data-id', element[opts.idAttribute]).addClass('fct-bs-col-md-4 pexSubtopic').on('click', onClick).appendTo(subtopicList);
// ... rendering the tree
var backButton = $('<button>').addClass('btn btn-default').text('Back').appendTo(browsercontainer);
backButton.on('click', onBackButtonClick);
}
function renderLevel3(parentContainer, selectedElement) {
$(parentContainer).html('');
var browsercontainer = $('<div>').addClass('myBrowserContainer').appendTo(parentContainer);
//... rendering the div ...
// for example like this with a onClick handler
var div = $('<div>').attr('data-id', element[opts.idAttribute]).addClass('fct-bs-col-md-4 pexSubtopic').on('click', onClick).appendTo(subtopicList);
// ... rendering the tree
var backButton = $('<button>').addClass('btn btn-default').text('Back').appendTo(browsercontainer);
backButton.on('click', onBackButtonClick);
}
function renderTopLevel(parentContainer) {
parentContainer.html('');
var browsercontainer = $('<div>').addClass('fct-page-pa fct-bs-container-fluid pexPAs myBrowserContainer').appendTo(parentContainer);
// rendering the top level display
}
getTreeData();
//top level rendering! Lower levels are rendered in event handlers.
$(this).each(function () {
renderTopLevel($(this));
});
return this;
};
// Private function for debugging.
function debug(debugText) {
if (window.console && window.console.log) {
window.console.log(debugText);
}
};
}(jQuery));
Just use one more class variable and pass this to it. Usually I call it self. So var self = this; in constructor of your plugin Class and you are good to go.
Object oriented way:
function YourPlugin(){
var self = this;
}
YourPlugin.prototype = {
constructor: YourPlugin,
clickHandler: function(){
// here the self works
}
}
Check this Fiddle
Or simple way of passing data to eventHandler:
$( "#foo" ).bind( "click", {
self: this
}, function( event ) {
alert( event.data.self);
});
You could use the jQuery proxy function:
$(yourElement).bind("click", $.proxy(this.yourFunction, this));
You can then use this in yourFunction as the this in your plugin.
Every time when I try to run this code in visual studio I get the above error. I am not sure what is going on. I know it's question like this, but I did not see this error in the answered in the solutions to this problem. Can anyone help me with this?
// display the lightbox
function lightbox() {
var insertContent = "<div id='chartCon'>Test</div>";
// jQuery wrapper (optional, for compatibility only)
(function ($) {
// add lightbox/shadow <div/>'s if not previously added
if ($('#lightbox').size() == 0) {
var theLightbox = $('<div id="lightbox" class="highcharts-container"/>');
var theShadow = $('<div id="lightbox-shadow"/>');
$(theShadow).click(function (e) {
closeLightbox();
});
$('body').append(theShadow);
$('body').append(theLightbox);
//$().keydown(function (e) {
// if (e.which == 27) {
// closeLightbox();
// }
//});
}
// remove any previously added content
$('#lightbox').empty();
// insert HTML content
if (insertContent != null) {
$('#lightbox').append(insertContent);
}
//create chart
var chart = $('#ChartContainer').highcharts('StockChart');
var annots = chart.exportData()
window.chartwidth = $('#ChartContainer').width();
//chart.destroy();
window.chartops.series = window.seriesOptions;
$('#lightbox').highcharts('StockChart', window.chartops);
$('#lightbox').highcharts('StockChart').importData(annots);
// move the lightbox to the current window top
$('#lightbox').css('top', $(window).scrollTop());
// display the lightbox
$('#lightbox').show();
$('#lightbox-shadow').show();
})(jQuery); // end jQuery wrapper
}
// close the lightbox
function closeLightbox() {
// jQuery wrapper (optional, for compatibility only)
(function ($) {
//export possibly changed annotations and reset chartwidth
var chart = $('#lightbox').highcharts('StockChart');
var annots = chart.exportData()
$('#ChartContainer').highcharts('StockChart').removeAllAnnotations();
$('#ChartContainer').highcharts('StockChart').importData(annots);
window.chartwidth = $('#ChartContainer').width();
// hide lightbox/shadow <div/>'s
$('#lightbox').hide();
$('#lightbox-shadow').hide();
// remove contents of lightbox in case a video or other content is actively playing
$('#lightbox').empty();
})(jQuery); // end jQuery wrapper
}
Sorry, this is the function giving me the error
init: function (chart) {
var rangeSelector = this,
options = chart.options.rangeSelector,
buttonOptions = options.buttons || [].concat(rangeSelector.defaultButtons),
selectedOption = options.selected,
blurInputs = rangeSelector.blurInputs = function () {
var minInput = rangeSelector.minInput,
maxInput = rangeSelector.maxInput;
if (minInput) {
minInput.blur();
}
if (maxInput) {
maxInput.blur();
}
};
I'd like to serialize and store Ace Session objects, so I can open a "File" and restore everything, value, selection, cursor position, mode, etc.
I've tried JSON.stringify(session) and it throws a circular error.
Any ideas?
the simplest version would be
var session = editor.session
state = {}
state.value = session.getValue();
state.selection = session.selection.toJSON()
state.options = session.getOptions()
state.mode = session.getMode().$id
state.folds = session.getAllFolds().map(function(fold) {
return {
start : fold.start,
end : fold.end,
placeholder : fold.placeholder
};
});
state.scrollTop = session.getScrollTop()
state.scrollLeft = session.getScrollLeft()
JSON.stringify(state)
and to restore
session.setValue(state.value)
session.selection.fromJSON(state.selection)
session.setOptions(state.options)
session.setMode(state.mode)
try {
state.folds.forEach(function(fold){
session.addFold(fold.placeholder,
Range.fromPoints(fold.start, fold.end));
});
} catch(e) {}
session.setScrollTop(state.scrollTop)
session.setScrollTop(state.scrollLeft)
this doesn't cover restoring undomanager which is doable but a little trickier. you can try to bump this issue https://github.com/ajaxorg/ace/issues/1452
My solution combined from 3 sources:
https://stackoverflow.com/a/20404474/630169
https://stackoverflow.com/a/28260919/630169
https://stackoverflow.com/a/34311510/630169
Moved session serialization code to a separate module:
editor-session.js:
var ace = require('brace');
var Range = ace.acequire('ace/range').Range;
var filterHistory = function(deltas) {
return deltas.filter(function (d) {
return d.group != "fold";
});
};
/** #param {AceAjax.Editor} editor */
function sessionToJson(editor)
{
return {
content: editor.getSession().getValue(),
selection: editor.getSelection().toJSON(),
options: editor.getOptions(),
mode: editor.session.getMode().$id,
scrollTop: editor.session.getScrollTop(),
scrollLeft: editor.session.getScrollLeft(),
history: {
undo: editor.session.getUndoManager().$undoStack.map(filterHistory),
redo: editor.session.getUndoManager().$undoStack.map(filterHistory)
},
folds: editor.session.getAllFolds().map(function(fold) {
return {
start : fold.start,
end : fold.end,
placeholder : fold.placeholder
};
})
}
}
/** #param {AceAjax.Editor} editor */
function jsonToSession(editor, state)
{
editor.session.setValue(state.content);
editor.selection.fromJSON(state.selection);
editor.session.setOptions(state.options);
editor.session.setMode(state.mode);
editor.session.setScrollTop(state.scrollTop);
editor.session.setScrollLeft(state.scrollLeft);
editor.session.$undoManager.$undoStack = state.history.undo;
editor.session.$undoManager.$redoStack = state.history.redo;
try {
state.folds.forEach(function(fold) {
editor.session.addFold(fold.placeholder, Range.fromPoints(fold.start, fold.end));
});
} catch(e) {console.log('Fold exception: ' + e)}
}
module.exports.sessionToJson = sessionToJson;
module.exports.jsonToSession = jsonToSession;
Browser side JS: processed by browserify
var ace = require('brace');
var state = require('./editor-session');
var editor = ace.edit('web-editor');
...
function saveEditorSession() {
localStorage.setItem('editorSession', JSON.stringify(state.sessionToJson(editor)));
}
editor.getSession().on("change", function () {
textarea.value = editor.getSession().getValue();
// Save editor session to localStorage
saveEditorSession();
// Send editor content to backend
ajax.saveContent(textarea.value, function (response) { });
});
editor.getSession().selection.on('changeSelection', saveEditorSession);
editor.getSession().selection.on('changeCursor', saveEditorSession);
editor.getSession().on('changeFold', saveEditorSession);
editor.getSession().on('changeScrollLeft', saveEditorSession);
editor.getSession().on('changeScrollTop', saveEditorSession);
I'm working on an addon to a forum and I get this error:
Error: Component returned failure code: 0x8007000e (NS_ERROR_OUT_OF_MEMORY) [nsIXPCComponents_Utils.evalInSandbox]
I read that the error means that the script goes into infinite loop until it fills the sandbox.
apparently the js file that leads to this error is script-compiler.js
this is the script:
var ddplus_gmCompiler={
// getUrlContents adapted from Greasemonkey Compiler
// http://www.letitblog.com/code/python/greasemonkey.py.txt
// used under GPL permission
//
// most everything else below based heavily off of Greasemonkey
// http://greasemonkey.mozdev.org/
// used under GPL permission
getUrlContents: function(aUrl){
var ioService=Components.classes["#mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
var scriptableStream=Components
.classes["#mozilla.org/scriptableinputstream;1"]
.getService(Components.interfaces.nsIScriptableInputStream);
var channel=ioService.newChannel(aUrl, null, null);
var input=channel.open();
scriptableStream.init(input);
var str=scriptableStream.read(input.available());
scriptableStream.close();
input.close();
return str;
},
isGreasemonkeyable: function(url) {
var scheme=Components.classes["#mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService)
.extractScheme(url);
return (
(scheme == "http" || scheme == "https" || scheme == "file") &&
!/hiddenWindow\.html$/.test(url)
);
},
contentLoad: function(e) {
var unsafeWin=e.target.defaultView;
if (unsafeWin.wrappedJSObject) unsafeWin=unsafeWin.wrappedJSObject;
var unsafeLoc=new XPCNativeWrapper(unsafeWin, "location").location;
var href=new XPCNativeWrapper(unsafeLoc, "href").href;
if (
ddplus_gmCompiler.isGreasemonkeyable(href)
&& ( /http:\/\/ddunlimited\.net/.test(href) )
&& true
) {
var script=ddplus_gmCompiler.getUrlContents(
'chrome://ddplus/content/ddplus.js'
);
ddplus_gmCompiler.injectScript(script, href, unsafeWin);
}
},
injectScript: function(script, url, unsafeContentWin) {
var sandbox, script, logger, storage, xmlhttpRequester;
var safeWin=new XPCNativeWrapper(unsafeContentWin);
sandbox=new Components.utils.Sandbox(safeWin);
var storage=new ddplus_ScriptStorage();
xmlhttpRequester=new ddplus_xmlhttpRequester(
unsafeContentWin, window//appSvc.hiddenDOMWindow
);
sandbox.window=safeWin;
sandbox.document=sandbox.window.document;
sandbox.unsafeWindow=unsafeContentWin;
// patch missing properties on xpcnw
sandbox.XPathResult=Components.interfaces.nsIDOMXPathResult;
// add our own APIs
sandbox.GM_addStyle=function(css) { ddplus_gmCompiler.addStyle(sandbox.document, css) };
sandbox.GM_setValue=ddplus_gmCompiler.hitch(storage, "setValue");
sandbox.GM_getValue=ddplus_gmCompiler.hitch(storage, "getValue");
// kick : aggiunta la funzione
sandbox.GM_remove=ddplus_gmCompiler.hitch(storage, "remove");
sandbox.GM_openInTab=ddplus_gmCompiler.hitch(this, "openInTab", unsafeContentWin);
sandbox.GM_xmlhttpRequest=ddplus_gmCompiler.hitch(
xmlhttpRequester, "contentStartRequest"
);
//unsupported
sandbox.GM_registerMenuCommand=function(){};
sandbox.GM_log=function(){};
sandbox.GM_getResourceURL=function(){};
sandbox.GM_getResourceText=function(){};
sandbox.__proto__=sandbox.window;
try {
this.evalInSandbox(
"(function(){"+script+"})()",
url,
sandbox);
} catch (e) {
var e2=new Error(typeof e=="string" ? e : e.message);
e2.fileName=script.filename;
e2.lineNumber=0;
//GM_logError(e2);
alert(e2);
}
},
evalInSandbox: function(code, codebase, sandbox) {
if (Components.utils && Components.utils.Sandbox) {
// DP beta+
Components.utils.evalInSandbox(code, sandbox);
} else if (Components.utils && Components.utils.evalInSandbox) {
// DP alphas
Components.utils.evalInSandbox(code, codebase, sandbox);
} else if (Sandbox) {
// 1.0.x
evalInSandbox(code, sandbox, codebase);
} else {
throw new Error("Could not create sandbox.");
}
},
openInTab: function(unsafeContentWin, url) {
var tabBrowser = getBrowser(), browser, isMyWindow = false;
for (var i = 0; browser = tabBrowser.browsers[i]; i++)
if (browser.contentWindow == unsafeContentWin) {
isMyWindow = true;
break;
}
if (!isMyWindow) return;
var loadInBackground, sendReferrer, referrer = null;
loadInBackground = tabBrowser.mPrefs.getBoolPref("browser.tabs.loadInBackground");
sendReferrer = tabBrowser.mPrefs.getIntPref("network.http.sendRefererHeader");
if (sendReferrer) {
var ios = Components.classes["#mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
referrer = ios.newURI(content.document.location.href, null, null);
}
tabBrowser.loadOneTab(url, referrer, null, null, loadInBackground);
},
hitch: function(obj, meth) {
var unsafeTop = new XPCNativeWrapper(unsafeContentWin, "top").top;
for (var i = 0; i < this.browserWindows.length; i++) {
this.browserWindows[i].openInTab(unsafeTop, url);
}
},
apiLeakCheck: function(allowedCaller) {
var stack=Components.stack;
var leaked=false;
do {
if (2==stack.language) {
if ('chrome'!=stack.filename.substr(0, 6) &&
allowedCaller!=stack.filename
) {
leaked=true;
break;
}
}
stack=stack.caller;
} while (stack);
return leaked;
},
hitch: function(obj, meth) {
if (!obj[meth]) {
throw "method '" + meth + "' does not exist on object '" + obj + "'";
}
var hitchCaller=Components.stack.caller.filename;
var staticArgs = Array.prototype.splice.call(arguments, 2, arguments.length);
return function() {
if (ddplus_gmCompiler.apiLeakCheck(hitchCaller)) {
return;
}
// make a copy of staticArgs (don't modify it because it gets reused for
// every invocation).
var args = staticArgs.concat();
// add all the new arguments
for (var i = 0; i < arguments.length; i++) {
args.push(arguments[i]);
}
// invoke the original function with the correct this obj and the combined
// list of static and dynamic arguments.
return obj[meth].apply(obj, args);
};
},
addStyle:function(doc, css) {
var head, style;
head = doc.getElementsByTagName('head')[0];
if (!head) { return; }
style = doc.createElement('style');
style.type = 'text/css';
style.innerHTML = css;
head.appendChild(style);
},
onLoad: function() {
var appcontent=window.document.getElementById("appcontent");
if (appcontent && !appcontent.greased_ddplus_gmCompiler) {
appcontent.greased_ddplus_gmCompiler=true;
appcontent.addEventListener("DOMContentLoaded", ddplus_gmCompiler.contentLoad, false);
}
},
onUnLoad: function() {
//remove now unnecessary listeners
window.removeEventListener('load', ddplus_gmCompiler.onLoad, false);
window.removeEventListener('unload', ddplus_gmCompiler.onUnLoad, false);
window.document.getElementById("appcontent")
.removeEventListener("DOMContentLoaded", ddplus_gmCompiler.contentLoad, false);
},
}; //object ddplus_gmCompiler
function ddplus_ScriptStorage() {
this.prefMan=new ddplus_PrefManager();
}
ddplus_ScriptStorage.prototype.setValue = function(name, val) {
this.prefMan.setValue(name, val);
}
ddplus_ScriptStorage.prototype.getValue = function(name, defVal) {
return this.prefMan.getValue(name, defVal);
}
ddplus_ScriptStorage.prototype.remove = function(name) {
return this.prefMan.remove(name);
}
window.addEventListener('load', ddplus_gmCompiler.onLoad, false);
window.addEventListener('unload', ddplus_gmCompiler.onUnLoad, false);
The user script is massive and available in this gist.
To be able to see the error:
install the addon
go to the message board at http://ddunlimited.net/
open any thread and open click the reply link
The message will appear as soon as the reply page loads.
in practice is a tool created specifically for a forum ... with the functions targeted to simplify the daily actions of the moderator. Now the forum has changed domain and tried to make it compatible with the new forum. I'm editing the js file with a simple text editor. ettengo the error when I edit the script that I posted above. if you do not touch this script ... some functions disappear and are no longer present.
someone can help me? thank you very much: D
OK, reproducible after all. The error in this case has a bogus message, as this isn't actually an OOM condition, but evalInSandbox() receiving a notification from the JS engine that the script was aborted (due to it being unresponsive) and evalInSandbox() not being able to tell the difference.
The reason is an infinite loop in your code at line 425 (cont.):
var max = textArea.parentNode.parentNode.clientHeight;
while (max == textArea.parentNode.parentNode.clientHeight)
textArea.rows++;
This loop whill never abort as the condition will never get false.