Created
February 23, 2012 03:13
-
-
Save hongymagic/1889742 to your computer and use it in GitHub Desktop.
Pre-fetcher, Pre-loader, Pre-fixer, Pre-mium stuff.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* vim: set noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 autoindent smartindent: */ | |
/* jslint devel: false, browser: true, node: true, eqeq: true, white: true, passfail: true, plusplus: false, regexp: true, maxerr: 50, indent: 4 */ | |
// | |
// Pre.js 1.0.0 | |
// | |
// (c) 20011-2012 David Hong, @hongymagic. | |
// Underscore is freely distributable under the MIT license. | |
// Portions of Pre.js are inspired or borrowed from Prototype, | |
// Oliver Steele's Functional, and DocumentCloud's Underscore.js. | |
// For all details and documentation: | |
// http://hongymagic.github.com/pre.js | |
// | |
// ## Global namespace, Pre | |
var Pre = (function () { | |
'use strict'; | |
var | |
Pre, | |
prototype, | |
// Regular expressions to help determine file type. Pre.js supports the | |
// following file types: | |
// | |
// 1. Images: jpg, jpeg, gif, bmp, gif, png | |
// 2. Audio: wav, weba, ogg, mp3, m4a, aiff | |
// | |
// #### TODO | |
// | |
// 1. JSON | |
// 2. HTML | |
// 3. SVG | |
types = { | |
"image": | |
{ | |
regex: /^.*\.(jpe?g|png|bmp|gif)$/i, | |
get: function (resource) { | |
var image = new Image(); | |
image.src = resource; | |
return image; | |
} | |
}, | |
"audio": | |
{ | |
regex: /^.*\.(wav|weba|ogg|mp3|m4a|aiff)$/i, | |
get: function (resource) { | |
var audio = new Audio(); | |
audio.preload = "auto"; | |
audio.src = resource; | |
return audio; | |
} | |
} | |
}, | |
// Returns the type of resource based on file types defined above. | |
type = function (resource) { | |
var rtype, matches; | |
for (rtype in types) { | |
if (types.hasOwnProperty(rtype)) { | |
matches = types[rtype].regex.exec(resource); | |
if (matches && matches.length > 1) { | |
return rtype; | |
} | |
} | |
} | |
throw new Error('Unable to determine extension type: ' + resource); | |
}, | |
// Cache inline functions for faster! performance! Woo yeah! | |
ArrayProto = Array.prototype, | |
ObjectProto = Object.prototype, | |
FuncProto = Function.prototype, | |
isArray = Array.isArray, | |
reduce = ArrayProto.reduce, | |
each = ArrayProto.each, | |
map = ArrayProto.map, | |
slice = ArrayProto.slice, | |
bind = FuncProto.bind, | |
toString = ObjectProto.toString, | |
// ### Custom inline functions | |
// Returns a flattened array, **usage**: | |
// | |
// flatten([1, 2, 3], [4, 5, 6]); | |
// => [1, 2, 3, 4, 5, 6]; | |
flatten = function (array, shallow) { | |
return array.reduce(function (memo, value) { | |
if (isArray(value)) { | |
return memo.concat(shallow ? value : flatten(value)); | |
} | |
memo[memo.length] = value; | |
return memo; | |
}, []); | |
}, | |
isFunction = function (func) { | |
return toString.call(func) == '[object Function]'; | |
}, | |
// Create an asset data for internal use | |
createAsset = function (resource, asset) { | |
return { | |
url: resource, | |
resource: resource, | |
asset: asset | |
}; | |
}, | |
// Create a wrapped Callback object for internal use | |
createCallback = function (callback, context) { | |
return { | |
context: context, | |
callback: callback | |
}; | |
}, | |
// Store downloaded assets locally, main advantage is for audio assets | |
// we can cheat and play it over and over again without recreating the | |
// <audio> (or Audio) object by setting its `currentTime` property to 0. | |
loaded = Object.create(null), | |
done = Object.create(null), | |
// Load an asset and store internally | |
load = function (resource, callback, context) { | |
var | |
rtype = type(resource), | |
object, | |
asset; | |
if (!isArray(loaded[rtype])) { | |
loaded[rtype] = []; | |
} | |
object = types[rtype].get(resource); | |
if (isFunction(callback)) { | |
object.addEventListener('load', bind.call(callback, context || object), false); | |
} | |
asset = createAsset(resource, object); | |
loaded[rtype].push(asset); | |
return asset; | |
}, | |
// Used by Pre instances to store and resolve events. It is a simple events | |
// handler | |
events = Object.create(null); | |
// ## The Pre class | |
// Define `Pre` class and store a reference to its `prototype` for further | |
// extension. Use it like a class object, **usage**: | |
// | |
// var game = new Pre; | |
// var game = Object.create(Pre.prototype); | |
prototype = (Pre = function (options) { | |
this.options = options; | |
}).prototype; | |
// ## Extending the Pre.prototype | |
// Start your prefetches, **usage**: | |
// | |
// Pre.fetch('image.jpg'); | |
// Pre.fetch('landscape.jpg', 'sun.png', 'clouds.png'); | |
// Pre.fetch(['landscape.jpg', 'sun.png', 'clouds.png']); | |
// | |
// TODO: | |
// | |
// - store each asset in its own category | |
// - start loading immediately! | |
prototype.fetch = function () { | |
var | |
resources = slice.call(arguments), | |
asset, | |
pre = this; | |
if (resources.length > 0) { | |
resources = flatten(resources, []); | |
} | |
resources.forEach(function (resource) { | |
asset = load(resource, function (event) { | |
this.resolve('load', arguments); | |
}, this); | |
this.resolve('fetch', resource, asset); | |
}, this); | |
return this; | |
}; | |
// **Alias**: `pre.load` | |
prototype.load = prototype.fetch; | |
// Create a deferred style event binding | |
// | |
// var game = new Pre; | |
// | |
// game.when('fetch', function (resource) { | |
// console.log('Currently fetching: ' + resource); | |
// }, Pre); | |
// | |
// Context is optional. | |
// | |
// You can also force resolve: | |
// | |
// game.resolve('fetch'); | |
prototype.when = function (event, callback, context) { | |
if (!isFunction(callback)) { | |
throw new Error('Cannot register a callback: typeof callback = ' + toString.call(callback)); | |
} | |
if (!events[event]) { | |
events[event] = []; | |
} | |
events[event].push(createCallback(callback, context)); | |
return this; | |
}; | |
// **Alias**: `pre.on` | |
prototype.on = prototype.when; | |
// Resolve an event, unlike Promises/A, an event can be triggered | |
// multiple times. | |
prototype.resolve = function (event /*, resource, asset */) { | |
if (!events[event]) { | |
return; | |
} | |
var | |
callbacks = events[event], | |
args = slice.call(arguments, 1); | |
callbacks.forEach(function (node) { | |
node.callback.apply(node.context || args[0], args); | |
}, this); | |
return this; | |
}; | |
// #### DEBUG ONLY | |
// | |
// Get the internal store | |
prototype.all = function () { | |
return loaded; | |
}; | |
return Pre; | |
}()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment