PDA

View Full Version : Ext.ux.lazyLoad JS on Demand, including GMaps



sosamv
10 Feb 2010, 10:37 PM
Hi Everyone! I recently created a lazyLoad Object to load JS files on demand, I know there are other classes in the forum but none of them satisfy my needs
Requirements:


Avoid at all cost storing JS code in the database.
Eval Function is Evil!
Need to know when the js code has been loaded.

The entire explanation is in blog (http://jerrysosa.blogspot.com/2010/02/dynamically-loading-any-js-file-on.html) (Couldnt make the color syntax highlight to work here) Please take a look and tell me what you think, hope this help ;)

It can load ANY js file including GMaps API.
here's the code:



/**
* Ext.ux.lazyLoad
*
* @author Jerry Sosa (sosamv) Published with the permission of Near It Services (nearitservices.com)
* @version 1.0v
* @date 10. February 2010
*
* @license Ext.ux.lazyLoad.js is licensed under the terms of
* the Open Source LGPL 3.0 license. Commercial use is permitted to the extent
* that the code/component(s) do NOT become part of another Open Source or Commercially
* licensed development library or toolkit without explicit permission.
*
* License details: http://www.gnu.org/licenses/lgpl.html
*/


Ext.namespace('Ext.ux.lazyLoad');
/********************************************************************
*@name Ext.ux.lazyLoad
* Load javascript files onDemand
**/
Ext.ux.lazyLoad = function () {

var debug = false;
var objects = {
gmap: {
loaded: false,
checkLoad: 'GMap2'
/*Variable that must exists to confirm load*/
,
url: 'http://maps.google.com/maps?file=api&v=2&key=ABQIAAAAZtXebGuEUyPUsb16TunzchQH1vzzQPdXCsLMkqVnPRtWvfSdoRToJrguncXU9Z0FgvbgReXeej9BDQ&sensor=false&async=2'
},
ctrlPropertiesGrid: {
loaded: false,
checkLoad: 'Ext.ux.customControl',
url: '/app/properties/Ext.ux.customControl.js'
}
//.....ADD HERE MORE FILES
}
return {
/*
* @name Ext.ux.lazyLoad.get
* Retrieve js files on demand if not loaded
* @params
* -jsKey {String}: Js File name to load
* -callback {function}: function to be executed after
* -scope {Object}: Object scope to run the callback function
**/
get: function (jsKey, callback, scope) {
/*Return if is not a valid object*/
if (!objects[jsKey]) return;

/*Run thread*/
var waitCounter = 0;
this.thread(jsKey, callback, scope, waitCounter, 'loadScript' + Ext.id());
}
/*
*@name Ext.ux.lazyLoad.thread
*Reusable thread to check if js file has been loaded
*@params
* -jsKey{String}: js key from the objects array
* -callback{Function}: function to execute when file is loaded
* -scope {Object}: Object scope to run the callback function
* -waitCointer{Integer}: Current cycle number
* -id{String}: Id used to identify the script tag
**/
,
thread: function (jsKey, callback, scope, waitCounter, id) {

/*If its loaded*/
if (this.isLoaded(jsKey)) {
callback.call(scope);
} else {
/*If script object hasnt been included, do it*/
if (!Ext.get(id)) {
/*If hasnt been loaded*/
/*Prepare url*/
var url = objects[jsKey].url;

this.head = document.getElementsByTagName('head').item(0);
script = document.createElement('script');
script.src = url;
script.type = 'text/javascript';
script.id = id;
this.head.appendChild(script);
}
if (debug) Ext.get('testdiv').insertHtml('beforeEnd', jsKey + ' wait counter:' + waitCounter + '<br>');
try {
var checkVar = eval(objects[jsKey].checkLoad);
objects[jsKey].loaded = true;
callback.call(scope);
this.clean(id);
return;
} catch(e) {
if (waitCounter++<10) {
/*Will check 10 times every 2 seconds if the js has been loaded*/
this.thread.defer(2000, this, [jsKey, callback, scope, waitCounter, id]);
} else {
this.clean(id);
alert("Problem loading libraries");
}
}

}
}
/*
* @name Ext.ux.lazyLoad.clean
* Removes recently used script tag from dom
**/
,
clean: function (id) {
var scriptTag = document.getElementById(id);
if (scriptTag) this.head.removeChild(scriptTag);
}
/*
* @name Ext.ux.lazyLoad.isLoaded
* Getter function to retrieve status
**/
,
isLoaded: function (jsKey) {
return objects[jsKey].loaded;
}
}
}();


/*USAGE EXAMPLE*/
Ext.onReady(function () {

Ext.ux.lazyLoad.get('gmap', function (obj) {
Alert("Google Maps Has Been Loaded");
//Your code goes here and will be executed after loading the js file
},
this);

});

Dumas
13 Feb 2010, 1:09 AM
Nice script.
Does this work in all mayor browsers?

Here some ideas:
* @version should contain an number
* @license Ext.ux.FileTreePanel => Ext.ux.lazyLoad.js ?
* I would sugget you set objects as public var instead of an private var like
Ext.ux.lazyLoad.objects = {...};
Or otherwise I would enable the option of loading new scripts with an add function
* I would call the clean function "unload" or "delete", clean suggests another behaviour

Maybe a stupid question: You wrote that you don't wanna use eval() and still you do, why?

Why do you delete the loaded script thread?

Thx for your contribution!
Roland

mystix
13 Feb 2010, 3:08 AM
one tip: you could replace all those document.get* methods with their Ext counterparts.

and another: if i'm not wrong, you should be able to replace that waitCounter with an Ext.util.TaskRunner.

HTH.

sosamv
13 Feb 2010, 1:58 PM
Thanx for your time guys =)

@Dumas Yes, it works for any browser, I had troubles setting a proper load event on the <script> dom object due to different browser behaviors and this approach worked perfect for IE6+ FF3+ Safari and Chrome.

Regarding the comments on the code, I just copied saki's disclaimer LOL, and I forgot to change that part.

Totally right about the part of putting the object's as globals, but since we're using the script tag cross domain trick to pull the scripts, I don't want anyone with firebug to start playing with the loader when the app goes live, anyways, you should know all of the scripts you´ll need to load on the app in advance hehe.

I do use eval to check if the variable exists and since it is on a private object, theres no security breach (i suppose :S). I dont like to use eval to load big scripts, that's what i meant, sorry.

And finally I remove the script dom object used uhm... just because haha, no reason in particular, I though it will keep my dom cleaner.


@mystix The application we're building is very very big and we have lots of customized controls doing some big calculations, the reason I decided to use the plain old document functions is to try to make it quicker since are native and most of them cross browser functions :s, but i'll keep making changes to this script and post it back =)

One more time, thanx for your time guys!

Dumas
23 Feb 2010, 8:18 AM
@Dumas Yes, it works for any browser....
cool



Totally right about the part of putting the object's as globals, but since we're using the script tag cross domain trick to pull the scripts, I don't want anyone with firebug to start playing with the loader when the app goes live, anyways, you should know all of the scripts you´ll need to load on the app in advance hehe.
As soon as someone has firebug he can do it anyway.....
I just thought the code would look cleaner and more understandable if the loader is just an function and you define the objects in the app, but if someone want's that behaviour, it's just a one liner ....

Your script is really cool, I'll probably use this one if I need lazy loading some time ;-)

regards
Roland