I am importing a set of notes into my webpage, this is to read a JSON file locally in a loop and append the read data into the main div. No problem till now. But then I'm producing a ckeditor instance beside each note for the client to become able to easily add comments to his note of interest. The comments are initially generated as several indexed empty div's in another HTML file, loaded into the ckeditor instances. However, all these happen in a really large for loop (I have almost 6000 notes to be loaded in a segmented manner using if conditions), and so now I'm engaged with the classic closure-loop problem. Have read several previous questions and answers foo this and other websites and tested a number of them to get rid of the closure-loop problem, but no success so far.
The related segment of my java script has the structure:
var q;
$.when(
$.ajax( ... loads the json file that contains the notes and set q=$.parseJSON(data) on success)
).then(function() {
for(var i in q) {
if(i is in a specific range){
... several lines of code for properly importing the notes ...
... and generating a place for the comments to appear as:
... +'<div id="CKEditor'+i+'" contenteditable="true" placeholder="Put your comment here!"></div>'
... which is appended to the main div of the webpage
... Now the main problematic part begins:
$('#temporary').empty(); // a hidden div defined somewhere in the page
var func = (function() {
var ilocal=i, tmp;
return function() {
tmp=document.getElementById('temporary').innerHTML;
alert(tmp);
CKEDITOR.instances['CKEditor'+ilocal].setData(tmp);
}
})();
$.when(
$('#temporary').load("NewComments.htm #verse-"+i)
).then(func);
};
};
CKEDITOR.disableAutoInline = true;
CKEDITOR.inlineAll();
})
maybe the problem is not for the loop but for the nested $.when().then(), any suggestion to resolve the issue?
The problem is that there is only a single $('#temporary') div in your page, which will be re-used and overwritten by every iteration. In particular, in your callback
document.getElementById('temporary').innerHTML;
…
CKEDITOR.instances['CKEditor'+ilocal]
the ilocal (and tmp) variables are indeed local to the IIFE and that particular iteration, but document.getElementById is global. It will return the same element every time.
A quick fix is to create a new element for every request, and assign it to tmp during the iteration (like you assign i to ilocal) instead of when the func is called.
A much better practice however would be not to use $('#temporary').load("NewComments.htm #verse-"+i) multiple times, and instead load the NewComments.htm only once per Ajax and process the result as you need.
Related
I've just written a script using the Javascript API so I can use it as a Rule to a Space. Whenever a document enters in a space, it basically checks document's name to get the parent directory it will be stored in (document's name contains parent's folder name), then creates future parent directory (if it doesn't exist), and finally moves the document into it.
I'm having troubles with this last step. Whenever I try to move the document into the recently created folder, I get the following error:
Node has been pasted into its own tree.
This is my code so far, which I think is pretty much auto-descriptive:
var fileName= document.properties.name;
var fields = fileName.split('.');
var parentName= fields[0];
var newNode=space.childByNamePath(parentName);
if (newNode === null) { //create folder and move document into it
newNode=space.createFolder(parentName); //works
document.move(newNode); //I'm getting the error here
}else{ //folder already exists, just move document into it
document.move(newNode); //here too
}
If I comment out the document.move(newNode); lines everything else works fine. Parent folder is successfully created but obviously the document keeps stored at the root of the current space, which is not what I need. Indeed I need to move it into the actual folder.
Am I doing something wrong? Any help would be much appreciated. Thanks.
UPDATE: Indeed I found out that the move() call inside the if scope it's working if I comment out the other call inside the else scope. So the error is being thrown at the else move() call. Also if I remove the else clause and put just one move() call after if scope, it also produces the same error.
You should run your rule as a background process, it will solve the problem ("Run rule in background" option).
This is a little hard to describe but here goes. I'm building a page that looks something like the back end to Azure where the user chooses from a menu on the left that loads an item, call it AllUsers for now, to the right of it. From AllUsers there are options for that item and some of those might load another to the left of it, we will call that User. From User you could open more items to the right.
I have it setup nicely with a JS object something like
var AdminPage=function(){
this.con="../controllers/admin/admin_con.php";
this.pageName=null;
this.childPage=null;
this.page=null;
this.deleteItem=function(){
//Recursively deletes pages to the right
if(this.childPage){
this.childPage.delete();
this.childPage=null;
}
if(this.page){
this.page.remove();
}
return this;
}
this.add=function(pageData){
if(this.childPage){
this.deleteItem();
}
this.childPage=new AdminPage();
this.childPage.getPage(pageData);
return this;
};
this.getPage=function(pageData){
var that=this;
this.pageName=pageData.page;
$.post(this.con, pageData,
function(data,status){
if(status==='success'){
$("#rightCol").append(data);
var num=$("#rightCol").children().length;
that.page=$("#rightCol").children()[num-1];
eval(pageData.page+"=that");
}
}
);
return this;
};
}
So each time a page is added it creates a new instance of the object for that item to work with. If that item is removed it recursively removes all items to the right of it as well.
The line "eval(pageData.page+"=that");" and yes I now that eval is evil and have read all about it, creates a new variable name from the name of the item that has been added. That item say the User item then uses that variable when even it needs to call on its instance of the script. This all works mostly perfectly.
Where it fails is if the User item opens another, call it Connections that shows all the connections for that user. I then want to be able to click on a connection and have it open another User item, this works as it should how ever the problem lies when the second User item is opened and creates a new instance of the AdminPage because the same script has just loaded and has the same name it over writes the JS variable from the previous one. Then then affects several things. If you close one of them it closes both for instance and if you close the first that was loaded it should close what is to the right of it however the childPage property is now empty so that doesn't work.
So now for the question..
Is there a better way to handle creating the variables or is there an all around better way all together that will not cause this problem at all?
Can I get around using the Evil eval and make everything right with the world?
Thanks for any suggestions that you may have.
So I have found an answer to the problem but I still don't like it much.
I have generated a unique id and appended it to the end of the name of the script that I was using as the variable.
Issues with this method is it is still using eval and now I have to pass the variable round trip when getting the new item so that I can use php to inject the variable name into the page.
Not perfect and there has to be a better way.
Here is the updated add function
this.add=function(pageData){
if(this.childPage){
this.deleteChildren();
}
this.childPage=new AdminPage();
this.childPage.id=pageData.page+"_"+(Math.floor(Math.random() * (999999999 - 100000000 + 1)) + 100000000);
eval(this.childPage.id+"=this.childPage");
this.childPage.getPage(pageData);
return this;
};
I want to give clients an HTML block they can include in their site, and this HTML will contain some table and image, plus a javascript that will make manipulations over the HTML block.
so I give them the HTML :
<a data-theme="1" data-srv="http://localhost:50987/" class="block1" href="http://myserver/payment/Details">outer information</a><script type="text/javascript" src="http://myserver/Scripts/checkout.js"></script>
in checkout.js I have included JQuery if no Jquery exists in document and do manipulation over the element $('a.block1') ... the problem is when someone puts this block of HTML more then once over the same page, I want that the client will not call "checkout.js" more then once,
I've tried declaring global var inside "checkout.js" and check if it's exists, it works good to stop doing the same manipulation more then once but I want to stop the call to JS al together .
Javascript runs after it loads, you can't stop the JS running, if it is referenced multiple times. It won't be loaded multiple times, so the overhead of it running again is basically nil.
To stop the behavior of the javascript happening again, just put the check at the top level of the file, put the rest of the file in the conditional, and write to a global variable to make sure you don't run again.
if (window._your_unique_id === undefined) {
window._your_unique_id = true;
// the rest of your javascript
}
that will mean nothing in the script runs. You can still define whatever you like in that if statement, though if you define functions and variables in there, you may have to explicitly put them on the window object, because they'll otherwise be local (but then, it is bad practice to have anything implicitly defined global anyway, so it shouldn't make any difference if your code is well structured).
Just deploy your code as a module.
Ie.
(function(window){
if(window.CheckoutModule) return;
// now you're certain there's no global module loaded
var CheckoutModule = window.CheckoutModule = {};
// you can, ie, add a jQuery check here.
if('undefined' != typeof jQuery) {
// do your jQuery thing here.
}
return ;
})(window, jQuery);
I've got about 30 web pages, all of them HTML forms. Each page has two or more different form elements - select, input text, checkboxes, text areas - along with various ui elements, popups, form validation etc. I'm trying to refactor the jquery used in the pages to use the DRY principle but am not sure how to do it. Here area few examples of some of the jquery used:
Example Code Block A:
$(".show-tool", _container).mouseover(function() {
$(this).nextAll(":hidden").css('display','block');
});
Example Code Block B:
$(".optional").blur(function(){
if ($(this).val() == '')
{
$(this).addClass('optional');
$(this).val('(Optional)');
}
});
Example Code Block C:
$('.howtoremain').click(function() {
$('.hiddendiv').slideToggle("10000");
if($(this).hasClass('howtoremain')) {
$(this).removeClass('howtoremain').addClass('howtoremain2');
}
else {
$(this).removeClass('howtoremain2').addClass('howtoremain');
}
});
All of these are contained in the document.ready. The actual code list above isn't that relevant. I'm trying to have each HTML page only include the jquery code that is relevant. For example page 1 might use code block A and B. Page 2 might use A,B,C,D,E, and F. Page 3 might use code block C and G. Rather than have one giant document.ready with every code block (which will probably cause bugs at some point anyway if one code block needs to be slightly different than another for the same form element), how do you code this? Have one javascript file per code block also seems lousy, as it would cause multiple hits to the server per page. I think I am trying to get at one big javascript file, but only initialize in the document.ready those functions that are relevant to each page.
In my projects, I have gone over to using multiple JS-Files per module and concatenating them into a closure within a build process. This is similar to what jQuery does in its build process (cf. intro.js, outro.js)
This way, I can use granular, DRY modules in smaller files, then concatenate them. A typical single module file might look like this for your Example B:
( function($) {
var subjects = $('.optional');
if ( subjects.length === 0 ) {
// this is a knockout criteria for this module, thus exit this enclosed function
return;
}
subjects.blur(function(){
if ($(this).val() == '')
{
$(this).addClass('optional');
$(this).val('(Optional)');
}
});
// now use whatever you need to initialise.
})($);
As you can see, I use the outer function not only to keep my scope clean, but much more important, to be able to cancel the module's initialisation as soon as I realise, it is not needed on the current page / event / ... - Of course, you may find several more efficient ways of determining whether or not each module should initialise itself.
On some projects, I have a build script to concatenate these modules within another closure which might look like this:
( function( window ) {
var $ = window.jQuery; //call me paranoid, but I like my vars clean
$(document).ready( function() {
// stuff the modules here, one after another, in any sensible order.
});
}(window);
In other projects, I am able to move the $(document).ready()-Bit into the modules where document.ready is needed, and listening to other Initialisation-Events within others, which feels a bit cleaner for me.
But anyway - having
a build process rather than a lot of single requests
several small, "one-thing-only"-Files to go into the build
a self-enforced "top-level lambda function" due to the intro/outro-Concatenation-Style I adapted from jQuery itself
has significantly improved my DRY-ness and, even, my JS code style.
Could someone explain the javascript that makes up Google's Website Optimiser Control script? Specifically: the first two lines, which seem to be empty functions, and why is the third function wrapped parentheses () ?
As far as I can tell this script is basically writing out a new <script> which presumably loads something for A/B testing.
function utmx_section(){}
function utmx(){}
(function() {
var k='0634742331',d=document,l=d.location,c=d.cookie;
function f(n) {
if(c) {
var i=c.indexOf(n+'=');
if (i>-1) {
var j=c.indexOf(';',i);
return escape(c.substring(i+n.length+1,j<0?c.length:j))
}
}
}
var x=f('__utmx'),xx=f('__utmxx'),h=l.hash;
d.write('<sc'+'ript src="'+'http'+(l.protocol=='https:'?'s://ssl':'://www')+'.google-analytics.com'+'/siteopt.js?v=1&utmxkey='+k+'&utmx='+(x?x:'')+'&utmxx='+(xx?xx:'')+'&utmxtime='+new Date().valueOf()+(h?'&utmxhash='+escape(h.substr(1)):'')+'" type="text/javascript" charset="utf-8"></sc'+'ript>')
}
)();
I've attempted to step through with the firebug debugger but it doesn't seem to like it. Any insights much appreciated.
Many thanks
inside anonymous function it shortens names of document and cookies inside it at first, function f(n) gets value of cookie under name n. Then Google reads its cookies and with help of d.write it loads its scripts (as I see they are related to Google Analytic). This way it makes On-Demand JavaScript loading... Actually you load these scripts all the time, Google just needs some additional parameters in url, so this is done this way - save parameters in cookie, which next time are used to get script again.
And finally back to the first two magic lines :) After Google loads its script (after executing d.write), there are some functions which uses utmx and utmx_section, as well as definition of these functions, or better to say overriding. I think they are empty at first just because another function can execute it before its real definition, and having empty functions nothing will happen (and no JS error), otherwise script would not work. E.g. after first iteration there is some data, which is used to make real definition of these functions and everything starts to work :)
The first 2 functions are in fact empty, and are probably overridden later on.
The third function is an anonymous self-executing function. The brackets are a convention to make you aware of the fact that it is self executing.
the "f" function looks up the value given to it in the document's cookies and returns it. Then a new script tag is written to document (and requested from server) with these values as part of its URL.