Skip to content

Instantly share code, notes, and snippets.

@mindplay-dk
Created December 1, 2010 22:25
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mindplay-dk/724347 to your computer and use it in GitHub Desktop.
Save mindplay-dk/724347 to your computer and use it in GitHub Desktop.
Dynamic loader for JS and CSS resources
/*
Version: 1.1
Developer: Rasmus Schultz <http://mindplay.dk>
License: GPL v3 <http://www.gnu.org/licenses/gpl-3.0-standalone.html>
Gist: <https://gist.github.com/724347>
Removing this notice from the source code would be bad karma.
*/
var Loader = {
q: [], // the Queue for pending items to be loaded
reg: {}, // a registry to ensure items are loaded only once
load: function(url, cb, context, driver, once) {
var dd = url.split('.').pop();
if (!Loader.drivers[dd]) dd = driver || 'js';
var p = new Loader.Proxy(
{ url: url, cb: cb || function(){}, reg: once, driver: dd, context: context }
);
Loader.q.push(p);
Loader.next();
return p;
},
once: function(url, cb, context, driver) {
this.load(url, cb, context, driver, 1);
},
next: function() {
for (i=0; i<Loader.q.length; i++) {
var l = Loader.q[i];
if (l.state == 1) return; // already loading
if (l.state == 0) return l.load(); // not loading (and not yet loaded)
}
}
}
Loader.Proxy = function(opt) {
this.driver = opt.driver;
this.context = opt.context || this;
this.url = opt.url;
this.reg = opt.reg;
this.state = 0; // inactive
this.cb = opt.cb;
this.load = function() {
if (this.reg && Loader.reg[this.url]) return this.loaded(); // already loaded once
this.state = 1; // loading
var hd = document.getElementsByTagName("head")[0];
var el = Loader.drivers[this.driver](this, this.url + (this.url.indexOf('?') == -1 ? '?' : '&') + new Date().getTime());
hd.appendChild(el);
}
this.loaded = function() {
this.state = 2; // loaded
if (this.reg) Loader.reg[this.url] = 1;
this.cb.call(this.context);
Loader.next();
}
}
Loader.drivers = {
js: function(proxy, url) {
var el = document.createElement('script');
el.type = 'text/javascript';
el.src = url;
var me = proxy;
if (el.attachEvent) { // IE
el.attachEvent('onreadystatechange', function() {
if (el.readyState == 'loaded' || el.readyState == 'complete') me.loaded();
});
} else { // DOM
el.onload = function() { me.loaded(); }
}
return el;
},
css: function(proxy, url) {
var el = document.createElement('link');
el.rel = 'stylesheet';
el.type = 'text/css';
el.href = url;
el.media = 'all';
new (function(link, proxy){
this.index = document.styleSheets.length;
this.link = link;
this.proxy = proxy;
var me = this;
this.check = function() {
try {
var s = document.styleSheets[me.index];
if ((s.rules || s.cssRules).length) { // DOM || FF
window.clearInterval(me.int);
me.proxy.loaded();
}
} catch (e) {};
}
this.int = window.setInterval(this.check, 100);
})(el, proxy);
return el;
}
}
@mindplay-dk
Copy link
Author

Loader.js

Dynamically loads JavaScript and CSS files on demand.

Features

Some requirements that this class fulfills:

  • Dynamic: load any JavaScript or CSS resource on-demand.
  • Cross-browser: tested and worked in IE6/7/8-beta, Firefox 2/3, Google Chrome, Opera 9.5, Safari on OSX and FireFox 3 on OSX.
  • Stand-alone: plain DOM, does not rely on any framework or external libraries.
  • Sequential: respects the order in which resources are requested - loading a class, and then a class that extends that class, is guaranteed to work.
  • Load-once: similar to PHP's require_once(), you can load things like classes, widgets and style-sheets only once.

Limitations

There is currently no error-handling - missing files and empty stylesheets will throw this script off.

Previous version of my loader used a method of injection that allowed scripts from foreign domains to load and execute within the page, without security restrictions. This version uses a different technique, where scripts are actually embedded from their original source, which means that cross-domain security restrictions may apply.

Usage

There are two methods for loading resources:

Loader.load( url, [callback-function], [context-object], [driver-name] );
Loader.once( url, [callback-function], [context-object], [driver-name] );

The load() method will allow you to load the same resource repeatedly, while the once() method will ensure that the same URL is only loaded once. (Note that the callback-function will be called on time - that is, when the source would have loaded; callbacks are guaranteed to fire in the order the resources were requested.)

Arguments for the two functions are identical:

  • url : required - relative (to your page) or absolute URL to JavaScript or CSS resource.
  • callback-function : optional callback-function - called when the resource (and any resources requested before it) has loaded.
  • context-object : optional object to use as the context (this) for the callback-function.
  • driver-name : optional driver-name, e.g. "css" or "js" - if unspecified, will try to determine the driver by file-extension, or fall back to "js".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment