summaryrefslogtreecommitdiff
path: root/jaws/src/assets.js
diff options
context:
space:
mode:
authorMichele Bini <michele.bini@gmail.com>2014-10-05 12:28:54 (GMT)
committerMichele Bini <michele.bini@gmail.com>2014-10-05 12:28:54 (GMT)
commit0cd85c8e3e4a4101f21ce2f269c987177f3d82b1 (patch)
treeecfcb56b7bdaf1a6261caff47a25510dbb25424f /jaws/src/assets.js
parent0453a80a6cceb79755d3187dd8f70dd5e50d58f1 (diff)
Import jaws source
Diffstat (limited to 'jaws/src/assets.js')
-rwxr-xr-xjaws/src/assets.js537
1 files changed, 537 insertions, 0 deletions
diff --git a/jaws/src/assets.js b/jaws/src/assets.js
new file mode 100755
index 0000000..6f61947
--- /dev/null
+++ b/jaws/src/assets.js
@@ -0,0 +1,537 @@
+var jaws = (function(jaws) {
+ /**
+ * @fileOverview jaws.assets properties and functions
+ *
+ * Loads and processes image, sound, video, and json assets
+ * (Used internally by JawsJS to create <b>jaws.assets</b>)
+ *
+ * @class Jaws.Assets
+ * @constructor
+ * @property {boolean} bust_cache Add a random argument-string to assets-urls when loading to bypass any cache
+ * @property {boolean} fuchia_to_transparent Convert the color fuchia to transparent when loading .bmp-files
+ * @property {boolean} image_to_canvas Convert all image assets to canvas internally
+ * @property {string} root Rootdir from where all assets are loaded
+ * @property {array} file_type Listing of file postfixes and their associated types
+ * @property {array} can_play Listing of postfixes and (during runtime) populated booleans
+ */
+ jaws.Assets = function Assets() {
+ if (!(this instanceof arguments.callee))
+ return new arguments.callee();
+
+ var self = this;
+
+ self.loaded = [];
+ self.loading = [];
+ self.src_list = [];
+ self.data = [];
+
+ self.bust_cache = false;
+ self.image_to_canvas = true;
+ self.fuchia_to_transparent = true;
+ self.root = "";
+
+ self.file_type = {};
+ self.file_type["json"] = "json";
+ self.file_type["wav"] = "audio";
+ self.file_type["mp3"] = "audio";
+ self.file_type["ogg"] = "audio";
+ self.file_type['m4a'] = "audio";
+ self.file_type['weba'] = "audio";
+ self.file_type['aac'] = "audio";
+ self.file_type['mka'] = "audio";
+ self.file_type['flac'] = "audio";
+ self.file_type["png"] = "image";
+ self.file_type["jpg"] = "image";
+ self.file_type["jpeg"] = "image";
+ self.file_type["gif"] = "image";
+ self.file_type["bmp"] = "image";
+ self.file_type["tiff"] = "image";
+ self.file_type['mp4'] = "video";
+ self.file_type['webm'] = "video";
+ self.file_type['ogv'] = "video";
+ self.file_type['mkv'] = "video";
+
+ self.can_play = {};
+
+ try {
+ var audioTest = new Audio();
+ self.can_play["wav"] = !!audioTest.canPlayType('audio/wav; codecs="1"').replace(/^no$/, '');
+ self.can_play["ogg"] = !!audioTest.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, '');
+ self.can_play["mp3"] = !!audioTest.canPlayType('audio/mpeg;').replace(/^no$/, '');
+ self.can_play["m4a"] = !!(audioTest.canPlayType('audio/x-m4a;') || audioTest.canPlayType('audio/aac;')).replace(/^no$/, '');
+ self.can_play["weba"] = !!audioTest.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/, '');
+ self.can_play["aac"] = !!audioTest.canPlayType('audio/aac;').replace(/^no$/, '');
+ self.can_play["mka"] = !!audioTest.canPlayType('audio/x-matroska;').replace(/^no$/, '');
+ self.can_play["flac"] = !!audioTest.canPlayType('audio/x-flac;').replace(/^no$/, '');
+ }
+ catch(e) {
+ }
+
+ try {
+ var videoTest = document.createElement('video');
+ self.can_play["mp4"] = !!videoTest.canPlayType('video/mp4;').replace(/^no$/, '');
+ self.can_play["webm"] = !!videoTest.canPlayType('video/webm; codecs="vorbis"').replace(/^no$/, '');
+ self.can_play["ogv"] = !!videoTest.canPlayType('video/ogg; codecs="vorbis"').replace(/^no$/, '');
+ self.can_play["mkv"] = !!videoTest.canPlayType('video/x-matroska;').replace(/^no$/, '');
+ }
+ catch(e) {
+ }
+
+ /**
+ * Returns the length of the resource list
+ * @public
+ * @returns {number} The length of the resource list
+ */
+ self.length = function() {
+ return self.src_list.length;
+ };
+
+ /**
+ * Set root prefix-path to all assets
+ *
+ * @example
+ * jaws.assets.setRoot("music/").add(["music.mp3", "music.ogg"]).loadAll()
+ *
+ * @public
+ * @param {string} path-prefix for all following assets
+ * @returns {object} self
+ */
+ self.setRoot = function(path) {
+ self.root = path
+ return self
+ }
+
+ /**
+ * Get one or more resources from their URLs. Supports simple wildcard (you can end a string with "*").
+ *
+ * @example
+ * jaws.assets.add(["song.mp3", "song.ogg"])
+ * jaws.assets.get("song.*") // -> Will return song.ogg in firefox and song.mp3 in IE
+ *
+ * @public
+ * @param {string|array} src The resource(s) to retrieve
+ * @returns {array|object} Array or single resource if found in cache. Undefined otherwise.
+ */
+ self.get = function(src) {
+ if (jaws.isArray(src)) {
+ return src.map(function(i) {
+ return self.data[i];
+ });
+ }
+ else if (jaws.isString(src)) {
+ // Wildcard? song.*, match against asset-srcs, make sure it's loaded and return content of first match.
+ if(src[src.length-1] === "*") {
+ var needle = src.replace("*", "")
+ for(var i=0; i < self.src_list.length; i++) {
+ if(self.src_list[i].indexOf(needle) == 0 && self.data[self.src_list[i]])
+ return self.data[self.src_list[i]];
+ }
+ }
+
+ // TODO: self.loaded[src] is false for supported files for some odd reason.
+ if (self.data[src]) { return self.data[src]; }
+ else { jaws.log.warn("No such asset: " + src, true); }
+ }
+ else {
+ jaws.log.error("jaws.get: Neither String nor Array. Incorrect URL resource " + src);
+ return;
+ }
+ };
+
+ /**
+ * Returns if specified resource is currently loading or not
+ * @public
+ * @param {string} src Resource URL
+ * @return {boolean|undefined} If resource is currently loading. Otherwise, undefined.
+ */
+ self.isLoading = function(src) {
+ if (jaws.isString(src)) {
+ return self.loading[src];
+ } else {
+ jaws.log.error("jaws.isLoading: Argument not a String with " + src);
+ }
+ };
+
+ /**
+ * Returns if specified resource is loaded or not
+ * @param src Source URL
+ * @return {boolean|undefined} If specified resource is loaded or not. Otherwise, undefined.
+ */
+ self.isLoaded = function(src) {
+ if (jaws.isString(src)) {
+ return self.loaded[src];
+ } else {
+ jaws.log.error("jaws.isLoaded: Argument not a String with " + src);
+ }
+ };
+
+ /**
+ * Returns lowercase postfix of specified resource
+ * @public
+ * @param {string} src Resource URL
+ * @returns {string} Lowercase postfix of resource
+ */
+ self.getPostfix = function(src) {
+ if (jaws.isString(src)) {
+ return src.toLowerCase().match(/.+\.([^?]+)(\?|$)/)[1];
+ } else {
+ jaws.log.error("jaws.assets.getPostfix: Argument not a String with " + src);
+ }
+ };
+
+ /**
+ * Determine type of file (Image, Audio, or Video) from its postfix
+ * @private
+ * @param {string} src Resource URL
+ * @returns {string} Matching type {Image, Audio, Video} or the postfix itself
+ */
+ function getType(src) {
+ if (jaws.isString(src)) {
+ var postfix = self.getPostfix(src);
+ return (self.file_type[postfix] ? self.file_type[postfix] : postfix);
+ } else {
+ jaws.log.error("jaws.assets.getType: Argument not a String with " + src);
+ }
+ }
+
+ /**
+ * Add URL(s) to asset listing for later loading
+ * @public
+ * @param {string|array|arguments} src The resource URL(s) to add to the asset listing
+ * @example
+ * jaws.assets.add("player.png")
+ * jaws.assets.add(["media/bullet1.png", "media/bullet2.png"])
+ * jaws.assets.add("foo.png", "bar.png")
+ * jaws.assets.loadAll({onload: start_game})
+ */
+ self.add = function(src) {
+ var list = arguments;
+ if(list.length == 1 && jaws.isArray(list[0])) list = list[0];
+
+ for(var i=0; i < list.length; i++) {
+ if(jaws.isArray(list[i])) {
+ self.add(list[i]);
+ }
+ else {
+ if(jaws.isString(list[i])) { self.src_list.push(list[i]) }
+ else { jaws.log.error("jaws.assets.add: Neither String nor Array. Incorrect URL resource " + src) }
+ }
+ }
+
+ return self;
+ };
+
+ /**
+ * Iterate through the list of resource URL(s) and load each in turn.
+ * @public
+ * @param {Object} options Object-literal of callback functions
+ * @config {function} [options.onprogress] The function to be called on progress (when one assets of many is loaded)
+ * @config {function} [options.onerror] The function to be called if an error occurs
+ * @config {function} [options.onload] The function to be called when finished
+ */
+ self.loadAll = function(options) {
+ self.load_count = 0;
+ self.error_count = 0;
+
+ if (options.onprogress && jaws.isFunction(options.onprogress))
+ self.onprogress = options.onprogress;
+
+ if (options.onerror && jaws.isFunction(options.onerror))
+ self.onerror = options.onerror;
+
+ if (options.onload && jaws.isFunction(options.onload))
+ self.onload = options.onload;
+
+ self.src_list.forEach(function(item) {
+ self.load(item);
+ });
+
+ return self;
+ };
+
+ /**
+ * Loads a single resource from its given URL
+ * Will attempt to match a resource to known MIME types.
+ * If unknown, loads the file as a blob-object.
+ *
+ * @public
+ * @param {string} src Resource URL
+ * @param {Object} options Object-literal of callback functions
+ * @config {function} [options.onload] Function to be called when assets has loaded
+ * @config {function} [options.onerror] Function to be called if an error occurs
+ * @example
+ * jaws.load("media/foo.png")
+ * jaws.load("http://place.tld/foo.png")
+ */
+ self.load = function(src, options) {
+ if(!options) options = {};
+
+ if (!jaws.isString(src)) {
+ jaws.log.error("jaws.assets.load: Argument not a String with " + src);
+ return;
+ }
+
+ var asset = {};
+ var resolved_src = "";
+ asset.src = src;
+ asset.onload = options.onload;
+ asset.onerror = options.onerror;
+ self.loading[src] = true;
+ var parser = RegExp('^((f|ht)tp(s)?:)?//');
+ if (parser.test(src)) {
+ resolved_src = asset.src;
+ } else {
+ resolved_src = self.root + asset.src;
+ }
+ if (self.bust_cache) {
+ resolved_src += "?" + parseInt(Math.random() * 10000000);
+ }
+
+ var type = getType(asset.src);
+ if (type === "image") {
+ try {
+ asset.image = new Image();
+ asset.image.asset = asset;
+ asset.image.addEventListener('load', assetLoaded);
+ asset.image.addEventListener('error', assetError);
+ asset.image.src = resolved_src;
+ } catch (e) {
+ jaws.log.error("Cannot load Image resource " + resolved_src +
+ " (Message: " + e.message + ", Name: " + e.name + ")");
+ }
+ }
+ else if (self.can_play[self.getPostfix(asset.src)]) {
+ if (type === "audio") {
+ try {
+ asset.audio = new Audio();
+ asset.audio.asset = asset;
+ asset.audio.addEventListener('error', assetError);
+ asset.audio.addEventListener('canplay', assetLoaded); // NOTE: assetLoaded can be called several times during loading.
+ self.data[asset.src] = asset.audio;
+ asset.audio.src = resolved_src;
+ asset.audio.load();
+ } catch (e) {
+ jaws.log.error("Cannot load Audio resource " + resolved_src +
+ " (Message: " + e.message + ", Name: " + e.name + ")");
+ }
+ }
+ else if (type === "video") {
+ try {
+ asset.video = document.createElement('video');
+ asset.video.asset = asset;
+ self.data[asset.src] = asset.video;
+ asset.video.setAttribute("style", "display:none;");
+ asset.video.addEventListener('error', assetError);
+ asset.video.addEventListener('canplay', assetLoaded);
+ document.body.appendChild(asset.video);
+ asset.video.src = resolved_src;
+ asset.video.load();
+ } catch (e) {
+ jaws.log.error("Cannot load Video resource " + resolved_src +
+ " (Message: " + e.message + ", Name: " + e.name + ")");
+ }
+ }
+ }
+
+ //Load everything else as raw blobs...
+ else {
+ // ... But don't load un-supported audio-files.
+ if(type === "audio" && !self.can_play[self.getPostfix(asset.src)]) {
+ assetSkipped(asset);
+ return self;
+ }
+
+ try {
+ var req = new XMLHttpRequest();
+ req.asset = asset;
+ req.onreadystatechange = assetLoaded;
+ req.onerror = assetError;
+ req.open('GET', resolved_src, true);
+ if (type !== "json")
+ req.responseType = "blob";
+ req.send(null);
+ } catch (e) {
+ jaws.log.error("Cannot load " + resolved_src +
+ " (Message: " + e.message + ", Name: " + e.name + ")");
+ }
+ }
+
+ return self;
+ };
+
+ /**
+ * Initial loading callback for all assets for parsing specific filetypes or
+ * optionally converting images to canvas-objects.
+ * @private
+ * @param {EventObject} event The EventObject populated by the calling event
+ * @see processCallbacks()
+ */
+ function assetLoaded(event) {
+ var asset = this.asset;
+ var src = asset.src;
+ var filetype = getType(asset.src);
+
+ try {
+ if (filetype === "json") {
+ if (this.readyState !== 4) {
+ return;
+ }
+ self.data[asset.src] = JSON.parse(this.responseText);
+ }
+ else if (filetype === "image") {
+ var new_image = self.image_to_canvas ? jaws.imageToCanvas(asset.image) : asset.image;
+ if (self.fuchia_to_transparent && self.getPostfix(asset.src) === "bmp") {
+ new_image = fuchiaToTransparent(new_image);
+ }
+ self.data[asset.src] = new_image;
+ }
+ else if (filetype === "audio" && self.can_play[self.getPostfix(asset.src)]) {
+ self.data[asset.src] = asset.audio;
+ }
+ else if (filetype === "video" && self.can_play[self.getPostfix(asset.src)]) {
+ self.data[asset.src] = asset.video;
+ } else {
+ self.data[asset.src] = this.response;
+ }
+ } catch (e) {
+ jaws.log.error("Cannot process " + src +
+ " (Message: " + e.message + ", Name: " + e.name + ")");
+ self.data[asset.src] = null;
+ }
+
+ /*
+ * Only increment load_count ONCE per unique asset.
+ * This is needed cause assetLoaded-callback can in certain cases be called several for a single asset...
+ * ..and not only Once when it's loaded.
+ */
+ if( !self.loaded[src]) self.load_count++;
+
+ self.loaded[src] = true;
+ self.loading[src] = false;
+
+ processCallbacks(asset, true, event);
+ }
+
+ /**
+ * Called when jaws asset-handler decides that an asset shouldn't be loaded
+ * For example, an unsupported audio-format won't be loaded.
+ *
+ * @private
+ */
+ function assetSkipped(asset) {
+ self.loaded[asset.src] = true;
+ self.loading[asset.src] = false;
+ self.load_count++;
+ processCallbacks(asset, true);
+ }
+
+ /**
+ * Increases the error count and calls processCallbacks with false flag set
+ * @see processCallbacks()
+ * @private
+ * @param {EventObject} event The EventObject populated by the calling event
+ */
+ function assetError(event) {
+ var asset = this.asset;
+ self.error_count++;
+ processCallbacks(asset, false, event);
+ }
+
+ /**
+ * Processes (if set) the callbacks per resource
+ * @private
+ * @param {object} asset The asset to be processed
+ * @param {boolean} ok If an error has occured with the asset loading
+ * @param {EventObject} event The EventObject populated by the calling event
+ * @see jaws.start() in core.js
+ */
+ function processCallbacks(asset, ok, event) {
+ var percent = parseInt((self.load_count + self.error_count) / self.src_list.length * 100);
+
+ if (ok) {
+ if(self.onprogress)
+ self.onprogress(asset.src, percent);
+ if(asset.onprogress && event !== undefined)
+ asset.onprogress(event);
+ }
+ else {
+ if(self.onerror)
+ self.onerror(asset.src, percent);
+ if(asset.onerror && event !== undefined)
+ asset.onerror(event);
+ }
+
+ if (percent === 100) {
+ if(self.onload) self.onload();
+
+ self.onprogress = null;
+ self.onerror = null;
+ self.onload = null;
+ }
+ }
+
+ /**
+ * Displays the progress of asset handling as an overall percentage of all loading
+ * (Can be overridden as jaws.assets.displayProgress = function(percent_done) {})
+ * @public
+ * @param {number} percent_done The overall percentage done across all resource handling
+ */
+ self.displayProgress = function(percent_done) {
+
+ if (!jaws.isNumber(percent_done))
+ return;
+
+ if (!jaws.context)
+ return;
+
+ jaws.context.save();
+ jaws.context.fillStyle = "black";
+ jaws.context.fillRect(0, 0, jaws.width, jaws.height);
+
+ jaws.context.fillStyle = "white";
+ jaws.context.strokeStyle = "white";
+ jaws.context.textAlign = "center";
+
+ jaws.context.strokeRect(50 - 1, (jaws.height / 2) - 30 - 1, jaws.width - 100 + 2, 60 + 2);
+ jaws.context.fillRect(50, (jaws.height / 2) - 30, ((jaws.width - 100) / 100) * percent_done, 60);
+
+ jaws.context.font = "11px verdana";
+ jaws.context.fillText("Loading... " + percent_done + "%", jaws.width / 2, jaws.height / 2 - 35);
+
+ jaws.context.font = "11px verdana";
+ jaws.context.fillStyle = "#ccc";
+ jaws.context.textBaseline = "bottom";
+ jaws.context.fillText("powered by www.jawsjs.com", jaws.width / 2, jaws.height - 1);
+
+ jaws.context.restore();
+ };
+ };
+
+ /**
+ * Make Fuchia (0xFF00FF) transparent (BMPs ONLY)
+ * @private
+ * @param {HTMLImageElement} image The Bitmap Image to convert
+ * @returns {CanvasElement} canvas The translated CanvasElement
+ */
+ function fuchiaToTransparent(image) {
+ if (!jaws.isDrawable(image))
+ return;
+
+ var canvas = jaws.isImage(image) ? jaws.imageToCanvas(image) : image;
+ var context = canvas.getContext("2d");
+ var img_data = context.getImageData(0, 0, canvas.width, canvas.height);
+ var pixels = img_data.data;
+ for (var i = 0; i < pixels.length; i += 4) {
+ if (pixels[i] === 255 && pixels[i + 1] === 0 && pixels[i + 2] === 255) { // Color: Fuchia
+ pixels[i + 3] = 0; // Set total see-through transparency
+ }
+ }
+
+ context.putImageData(img_data, 0, 0);
+ return canvas;
+ }
+
+ jaws.assets = new jaws.Assets();
+ return jaws;
+})(jaws || {});
+