Skip to content

Instantly share code, notes, and snippets.

@Olical
Created September 22, 2011 12:07
Show Gist options
  • Save Olical/1234631 to your computer and use it in GitHub Desktop.
Save Olical/1234631 to your computer and use it in GitHub Desktop.
Preloader.js - Preload all assets, ideally for a game, using MooTools

About

This class allows you to preload all of your images, sounds, videos, JSON, JavaScript and CSS files with one call. It provides you with progress reports and lets you know when everything is done.

It requires MooTools and MooTools More with the Assets and Request.JSONP boxes ticked. I have attached a copy of the required MooTools More version to this gist.

Example

You can find an example here on jsFiddle.

Here is the code used in that example.

var loader = new Preloader({
    images: {
        small: 'http://placehold.it/100x100',
        large: 'http://placehold.it/200x200'
    },
    sounds: {
        nyan: 'http://upload.wikimedia.org/wikipedia/en/2/2a/Nyan_cat.ogg'
    },
    videos: {
        bear: 'http://www.w3schools.com/html5/movie.ogg'
    },
    json: {
        jsfiddle: {
            url: '/echo/json/',
            data: {
                json: JSON.encode({ foo: 'bar', bar: 'foo' })
            },
            method: 'post'
        },
        test: '/echo/json/'
    },
    jsonp: {
        twitter: 'https://api.twitter.com/1/statuses/user_timeline.json?screen_name=OliverCaldwell'
    },
    scripts: {
        EventEmitter: 'https://raw.github.com/Wolfy87/EventEmitter/master/src/EventEmitter.min.js'
    },
    stylesheets: {
        reset: 'http://meyerweb.com/eric/tools/css/reset/reset.css'
    },
    onProgress: function(current, outOf, percentage) {
        $('output').grab(new Element('li', {
            text: current + '/' + outOf + ' (' + percentage.floor() + '%)'
        }));
    },
    onComplete: function(assets, amount, time) {
        $('output').grab(new Element('li', {
            text: 'Done loading ' + amount + ' assets in ' + (time / 1000) + ' seconds.'
        }));
    }
});

loader.load();

This tests every aspect of the preloader.

Documentation

All you need to do is set up your preloader and then call the load() method. The setup involves creating an instance of the class like so.

var preloader = new Preloader();

But you pass an object of options as a parameter. As you can see in the example, these are just strings leading to what you wish to load under the appropriate type. The available types are as follows.

  • images
  • sounds
  • videos
  • json
  • jsonp
  • scripts
  • stylesheets

With json and jsonp you can pass an object rather than a string which will be used as the options in the Request.JSON/JSONP call.

Every time something is finished loading the progress event is fired. This passes the following arguments to the listeners.

  • The amount of assets loaded currently.
  • The full amount of assets that will be loaded.
  • The percentage of assets loaded.

When everything is done the complete event is fired. This passes these arguments to the listeners.

  • The object of assets, it is like a copy of the options you passed but the URLs are replaced with the elements and text that you requested.
  • The amount of assets loaded, the same number that is passed as the second argument to the progress event.
  • The amount of milliseconds that have elapsed since the loading began.

Author

This class was written by Oliver Caldwell.

// MooTools: the javascript framework.
// Load this file's selection again by visiting: http://mootools.net/more/1b7ad40ac54224a2771999b12627d2db
// Or build this file again with packager using: packager build More/Request.JSONP More/Assets
/*
---
copyrights:
- [MooTools](http://mootools.net)
licenses:
- [MIT License](http://mootools.net/license.txt)
...
*/
MooTools.More={version:"1.4.0.1",build:"a4244edf2aa97ac8a196fc96082dd35af1abab87"};Request.JSONP=new Class({Implements:[Chain,Events,Options],options:{onRequest:function(a){if(this.options.log&&window.console&&console.log){console.log("JSONP retrieving script with url:"+a);
}},onError:function(a){if(this.options.log&&window.console&&console.warn){console.warn("JSONP "+a+" will fail in Internet Explorer, which enforces a 2083 bytes length limit on URIs");
}},url:"",callbackKey:"callback",injectScript:document.head,data:"",link:"ignore",timeout:0,log:false},initialize:function(a){this.setOptions(a);},send:function(c){if(!Request.prototype.check.call(this,c)){return this;
}this.running=true;var d=typeOf(c);if(d=="string"||d=="element"){c={data:c};}c=Object.merge(this.options,c||{});var e=c.data;switch(typeOf(e)){case"element":e=document.id(e).toQueryString();
break;case"object":case"hash":e=Object.toQueryString(e);}var b=this.index=Request.JSONP.counter++;var f=c.url+(c.url.test("\\?")?"&":"?")+(c.callbackKey)+"=Request.JSONP.request_map.request_"+b+(e?"&"+e:"");
if(f.length>2083){this.fireEvent("error",f);}Request.JSONP.request_map["request_"+b]=function(){this.success(arguments,b);}.bind(this);var a=this.getScript(f).inject(c.injectScript);
this.fireEvent("request",[f,a]);if(c.timeout){this.timeout.delay(c.timeout,this);}return this;},getScript:function(a){if(!this.script){this.script=new Element("script",{type:"text/javascript",async:true,src:a});
}return this.script;},success:function(b,a){if(!this.running){return;}this.clear().fireEvent("complete",b).fireEvent("success",b).callChain();},cancel:function(){if(this.running){this.clear().fireEvent("cancel");
}return this;},isRunning:function(){return !!this.running;},clear:function(){this.running=false;if(this.script){this.script.destroy();this.script=null;
}return this;},timeout:function(){if(this.running){this.running=false;this.fireEvent("timeout",[this.script.get("src"),this.script]).fireEvent("failure").cancel();
}return this;}});Request.JSONP.counter=0;Request.JSONP.request_map={};var Asset={javascript:function(d,b){if(!b){b={};}var a=new Element("script",{src:d,type:"text/javascript"}),e=b.document||document,c=b.onload||b.onLoad;
delete b.onload;delete b.onLoad;delete b.document;if(c){if(typeof a.onreadystatechange!="undefined"){a.addEvent("readystatechange",function(){if(["loaded","complete"].contains(this.readyState)){c.call(this);
}});}else{a.addEvent("load",c);}}return a.set(b).inject(e.head);},css:function(d,a){if(!a){a={};}var b=new Element("link",{rel:"stylesheet",media:"screen",type:"text/css",href:d});
var c=a.onload||a.onLoad,e=a.document||document;delete a.onload;delete a.onLoad;delete a.document;if(c){b.addEvent("load",c);}return b.set(a).inject(e.head);
},image:function(c,b){if(!b){b={};}var d=new Image(),a=document.id(d)||new Element("img");["load","abort","error"].each(function(e){var g="on"+e,f="on"+e.capitalize(),h=b[g]||b[f]||function(){};
delete b[f];delete b[g];d[g]=function(){if(!d){return;}if(!a.parentNode){a.width=d.width;a.height=d.height;}d=d.onload=d.onabort=d.onerror=null;h.delay(1,a,a);
a.fireEvent(e,a,1);};});d.src=a.src=c;if(d&&d.complete){d.onload.delay(1);}return a.set(b);},images:function(c,b){c=Array.from(c);var d=function(){},a=0;
b=Object.merge({onComplete:d,onProgress:d,onError:d,properties:{}},b);return new Elements(c.map(function(f,e){return Asset.image(f,Object.append(b.properties,{onload:function(){a++;
b.onProgress.call(this,a,e,f);if(a==c.length){b.onComplete();}},onerror:function(){a++;b.onError.call(this,a,e,f);if(a==c.length){b.onComplete();}}}));
}));}};
/**
* @preserve Class for loading assets (images, sound, video, json, jsonp, javascript, css)
* Provides an x out of y value and a percentage on progress
* And lets you know when everything is done
* Great for preloading all game assets
*
* Requires MooTools and MooTools More (Assets, Reqest.JSONP)
*
* Copyright Oliver Caldwell 2011 (olivercaldwell.co.uk)
*/
var Preloader = new Class({
Implements: [Options, Events],
options: {
images: {},
sounds: {},
videos: {},
json: {},
jsonp: {},
scripts: {},
stylesheets: {}
},
initialize: function(options) {
this.setOptions(options);
},
load: function() {
// Initialise variables
var assetCount = 0,
completedCount = 0,
startTime = new Date().getTime(),
loaded = {
images: {},
sounds: {},
videos: {},
json: {},
jsonp: {},
scripts: {},
stylesheets: {}
},
handleProgress = function() {
completedCount += 1;
this.fireEvent('progress', [completedCount, assetCount, 100 / assetCount * completedCount]);
if(completedCount === assetCount) {
this.fireEvent('complete', [loaded, assetCount, new Date().getTime() - startTime]);
}
}.bind(this);
// Fire the start event
this.fireEvent('start', [assetCount]);
// Count all items
Object.each(this.options, function(assetObject) {
assetCount += Object.getLength(assetObject);
});
// Load images
Object.each(this.options.images, function(path, name) {
loaded.images[name] = Asset.image(path, {
onLoad: handleProgress
});
});
// Load sounds
Object.each(this.options.sounds, function(path, name) {
loaded.sounds[name] = new Element('audio');
loaded.sounds[name].addEventListener('canplaythrough', handleProgress, false);
loaded.sounds[name].set('src', path);
loaded.sounds[name].load();
});
// Load videos
Object.each(this.options.videos, function(path, name) {
loaded.videos[name] = new Element('video');
loaded.videos[name].addEventListener('canplaythrough', handleProgress, false);
loaded.videos[name].set('src', path);
loaded.videos[name].load();
});
// Load JSON
Object.each(this.options.json, function(settings, name) {
var request = new Request.JSON((typeof settings === 'object') ? settings : {url:settings});
request.addEvent('success', function(loadedJson) {
loaded.json[name] = loadedJson;
handleProgress();
});
request.send();
});
// Load JSONP
Object.each(this.options.jsonp, function(settings, name) {
var request = new Request.JSONP((typeof settings === 'object') ? settings : {url:settings});
request.addEvent('success', function(loadedJson) {
loaded.jsonp[name] = loadedJson;
handleProgress();
});
request.send();
});
// Load scripts
Object.each(this.options.scripts, function(path, name) {
loaded.scripts[name] = Asset.javascript(path, {
onLoad: handleProgress
});
});
// Load stylesheets
Object.each(this.options.stylesheets, function(path, name) {
loaded.stylesheets[name] = Asset.css(path);
handleProgress();
});
}
});
/*
Class for loading assets (images, sound, video, json, jsonp, javascript, css)
Provides an x out of y value and a percentage on progress
And lets you know when everything is done
Great for preloading all game assets
Requires MooTools and MooTools More (Assets, Reqest.JSONP)
Copyright Oliver Caldwell 2011 (olivercaldwell.co.uk)
*/
var Preloader=new Class({Implements:[Options,Events],options:{images:{},sounds:{},videos:{},json:{},jsonp:{},scripts:{},stylesheets:{}},initialize:function(c){this.setOptions(c)},load:function(){var c=0,f=0,g=(new Date).getTime(),d={images:{},sounds:{},videos:{},json:{},jsonp:{},scripts:{},stylesheets:{}},e=function(){f+=1;this.fireEvent("progress",[f,c,100/c*f]);f===c&&this.fireEvent("complete",[d,c,(new Date).getTime()-g])}.bind(this);this.fireEvent("start",[c]);Object.each(this.options,function(a){c+=
Object.getLength(a)});Object.each(this.options.images,function(a,b){d.images[b]=Asset.image(a,{onLoad:e})});Object.each(this.options.sounds,function(a,b){d.sounds[b]=new Element("audio");d.sounds[b].addEventListener("canplaythrough",e,!1);d.sounds[b].set("src",a);d.sounds[b].load()});Object.each(this.options.videos,function(a,b){d.videos[b]=new Element("video");d.videos[b].addEventListener("canplaythrough",e,!1);d.videos[b].set("src",a);d.videos[b].load()});Object.each(this.options.json,function(a,
b){var c=new Request.JSON(typeof a==="object"?a:{url:a});c.addEvent("success",function(a){d.json[b]=a;e()});c.send()});Object.each(this.options.jsonp,function(a,b){var c=new Request.JSONP(typeof a==="object"?a:{url:a});c.addEvent("success",function(a){d.jsonp[b]=a;e()});c.send()});Object.each(this.options.scripts,function(a,b){d.scripts[b]=Asset.javascript(a,{onLoad:e})});Object.each(this.options.stylesheets,function(a,b){d.stylesheets[b]=Asset.css(a);e()})}});
@newbie78
Copy link

thanx very match for the preloader!
but a found the bug
https://developer.mozilla.org/en-US/docs/Configuring_servers_for_Ogg_media
for ff and ogg audio
need modify audio preloder like this:

        // Load sounds
        Object.each(this.options.sounds, function(path, name) {
            loaded.sounds[name]=new Element('audio',{
                preload:'auto',
                src:path
            });
            loaded.sounds[name].addEventListener('canplaythrough', handleProgress, false);
        });

maybe for ogv too
but i'm didn't test yet

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