diff options
author | Bryan Berry <bryan@olenepal.org> | 2009-11-13 21:34:51 (GMT) |
---|---|---|
committer | Bryan Berry <bryan@olenepal.org> | 2009-11-13 21:34:51 (GMT) |
commit | f44e4c08c4629c26c2e079b132a0534d54509f3c (patch) | |
tree | 1631d8a986435c93d3ba10f0baf937b6bdfe183d | |
parent | ae7313eb6505c4e3ef0be3df73d7f2f87bffd07c (diff) |
almost done w/ kMedia
-rwxr-xr-x | js/karma.js | 519 | ||||
-rwxr-xr-x | tests/tests.js | 39 |
2 files changed, 334 insertions, 224 deletions
diff --git a/js/karma.js b/js/karma.js index 12f9472..0e8fa6d 100755 --- a/js/karma.js +++ b/js/karma.js @@ -1,245 +1,346 @@ - if(!this.exports) { exports = {}; } - - -var Karma = exports.Karma = function (options) { - if ( Karma.KarmaRoot) { - return Karma.KarmaRoot; - } else { - return Karma.create(Karma.karma).init(options); - } -}; - -Karma.KarmaRoot = null; - - //helper functions, all in the Karma namespace - - //This emulates the Object.create method in ecmascript 5 spec - //This isn't a full implementation as it doesn't support - //Object.defineProperty - //This has the same functionality as Crockford's beget method - //and this primary building block for prototypal inheritance in - //this library - Karma.create = function (object){ - function F () {}; - F.prototype = object; - return new F(); - }; - - //this is a shallow copy - Karma.clone = function (object){ - var copy = {}; - for ( var i in object ) { - copy[i] = object[i]; - } - return copy; - }; - - //this copies all the enumerable properties in source to target - Karma.objectPlus = function (target, source){ - for ( var i in source){ - if (source.hasOwnProperty(i)){ - target[i] = source[i]; + + + var Karma = exports.Karma = function (options) { + if ( Karma.KarmaRoot) { + return Karma.KarmaRoot; + } else { + return Karma.create(Karma.karma).init(options); } - } - return target; - }; - - //Creates a new object that is a prototype of the first argument - // then extends it with the properties of the second argument - Karma.copyObjectPlus = function (parent1, parent2){ - function F () {}; - F.prototype = parent1; - var G = new F(); - return Karma.objectPlus(G, parent2); - }; + }; + + Karma.KarmaRoot = null; + + //helper functions, all in the Karma namespace + + //This emulates the Object.create method in ecmascript 5 spec + //This isn't a full implementation as it doesn't support + //Object.defineProperty + //This has the same functionality as Crockford's beget method + //and this primary building block for prototypal inheritance in + //this library + Karma.create = function (object){ + function F () {}; + F.prototype = object; + return new F(); + }; + + //this is a shallow copy + Karma.clone = function (object){ + var copy = {}; + for ( var i in object ) { + copy[i] = object[i]; + } + return copy; + }; + + //this copies all the enumerable properties in source to target + Karma.objectPlus = function (target, source){ + for ( var i in source){ + if (source.hasOwnProperty(i)){ + target[i] = source[i]; + } + } + return target; + }; + //Creates a new object that is a prototype of the first argument + // then extends it with the properties of the second argument + Karma.copyObjectPlus = function (parent1, parent2){ + function F () {}; + F.prototype = parent1; + var G = new F(); + return Karma.objectPlus(G, parent2); + }; - //These make* commands update the countTotalAssets and - //countNotLoaded when they first create each asset, then - //decrement countNotloaded as they are loaded - Karma.makeImages = function (images){ - }; + //These make* commands update the countTotalAssets and + //countNotLoaded when they first create each asset, then + //decrement countNotloaded as they are loaded + Karma.makeImages = function (images){ - Karma.makeSounds = function (sounds){ + }; - }; + Karma.makeSounds = function (sounds){ - Karma.makeVideos = function (videos){ + }; - }; + Karma.makeVideos = function (videos){ - Karma.makeSvgs = function (svgs){ + }; - }; + Karma.makeSvgs = function (svgs){ - Karma.makeSurfaces = function (surfaces){ + }; - }; + Karma.makeSurfaces = function (surfaces){ - - Karma.karma = { - locale : undefined, - _localized : false, - _localePath : "", - images : [], - videos : [], - sounds : [], - svgs : [], - _counters : { total : 0, errors : 0, loaded }, + }; - //init initializes all the assets passed to Karma, that's it - //it returns 'this' so it can be used for function chaining - init: function(options) { - this.name = "karma"; - Karma.KarmaRoot = this; - - //set up message that show count of assets loaded - var loaderDiv = document.createElement('div') - loaderDiv.innerHTML = '<div id=\"karma-loader\">Karma is \ + + Karma.karma = { + locale : undefined, + _localized : false, + _localePath : "", + images : [], + videos : [], + sounds : [], + svgs : [], + _counters : { total : 0, errors : 0, loaded : 0}, + + //init initializes all the assets passed to Karma, that's it + //it returns 'this' so it can be used for function chaining + init: function(options) { + this.name = "karma"; + Karma.KarmaRoot = this; + + //set up message that show count of assets loaded + var loaderDiv = document.createElement('div') + loaderDiv.innerHTML = '<div id=\"karma-loader\">Karma is \ loading ...<div id=\"karma-loader\" class=\"status\"></div></div>'; - document.body.appendChild(loaderDiv); - this.statusDiv = document.getElementById("karma-loader"); - - //regular expression that matches the name of aprivate property - // the karma object - var regexPrivate = new RegExp('^_.*'); - - for ( var option in options ) { - if (option === "images" || option === "sounds" || option === - "svgs" || option === "videos" || option === "surfaces"){ - - if(!(options[option] instanceof Array)){ - throw new Error("" + option + " must be an array"); - } else if (options[option].length === 0){ + document.body.appendChild(loaderDiv); + this.statusDiv = document.getElementById("karma-loader"); + + //create an ordered list to hold any error messages that pop-up + var ol = document.createElement('ol'); + this.statusDiv.appendChild(ol); + + + //regular expression that matches the name of aprivate property + // the karma object + var regexPrivate = new RegExp('^_.*'); + + for ( var option in options ) { + if (option === "images" || option === "sounds" || option === + "svgs" || option === "videos" || option === "surfaces"){ + + if(!(options[option] instanceof Array)){ + throw new Error("" + option + " must be an array"); + } else if (options[option].length === 0){ + continue; + } + } else if (regexPrivate.test(option)){ + //don't overwrite a private property of karma object continue; } - } else if (regexPrivate.test(option)){ - //don't overwrite a private property of karma object - continue; + + switch (option){ + case "locale": + if (this.isValidLocale(options[option])){ + this.locale = this.normalizeLocale(options[option]); + this._localized = true; + options._localePath = computeLocalePath(this.locale); + } else { + throw new Error("locale provided to karma.init() is invalid"); + } + + break; + case "images": + options[option]._type = 'image'; + Karma.makeImages(options[option]); + break; + case "sounds": + options[option]._type = 'sound'; + Karma.makeSounds(options[option]); + break; + case "videos": + options[option]._type = 'video'; + Karma.makeVideos(options[option]); + break; + case "svgs": + options[option]._type = 'svg'; + Karma.makeSvgs(options[option]); + break; + case "surfaces": + options[option]._type = 'surface'; + Karma.makeSurfaces(options[option]); + break; + } + } + return this; + }, + + //ready checks to see if all assets loaded, then runs lesson code + ready : function( cb ) { + that = this; + if (!Karma.KarmaRoot){ + throw new Error("Karma.karma not initialized"); } - switch (option){ - case "locale": - if (this.isValidLocale(options[option])){ - this.locale = this.normalizeLocale(options[option]); - this._localized = true; - options._localePath = computeLocalePath(this.locale); - } else { - throw new Error("locale provided to karma.init() is invalid"); - } - - break; - case "images": - Karma.makeImages(options[option]); - break; - case "sounds": - Karma.makeSounds(options[option]); - break; - case "videos": - Karma.makeVideos(options[option]); - break; - case "svgs": - Karma.makeSvgs(options[option]); - break; - case "surfaces": - Karma.makeSurfaces(options[option]); - break; + if (this._counters.loaded !== this._counters.total){ + setTimeout(function(){ that.ready(cb);}, 100); + } else if (cb) { + //hide that loader status + this.statusDiv.setAttribute('style', 'display:none;'); + cb(); + } else if (!cb) { + //if no options passed, show it works message + this.showStarterMessage(); } - } - return this; - }, - - //ready checks to see if all assets loaded, then runs lesson code - ready : function( cb ) { - that = this; - if (!Karma.KarmaRoot){ - throw new Error("Karma.karma not initialized"); - } + + return this; + }, + + //Display Apache-like "It works" message if no options + showStarterMessage : function (){ + var starterMsg = document.createElement('div'); + starterMsg.setAttribute('id', 'starterMsg'); + starterMsg.innerHTML = "<h1>It Works</h1>"; + document.body.appendChild(starterMsg); + }, + + statusUpdate : function (errorMsg) { + var loaded = this._counters.loaded; + var total = this._counters.total; + var errors = this._counters.total; + this.statusDiv.innerText = "" + loaded + "/" total + + (errors > 0 ? " [ "+errors+" ]":''); + if (errorMsg) { + var liError = document.createElement('li'); + liError.innerText = errorMsg; + this.statusDiv.ol.appendChild(liError); + } + }, - if (this._counters.loaded !== this._counters.total){ - setTimeout(function(){ that.ready(cb);}, 100); - } else if (cb) { - //hide that loader status - this.statusDiv.setAttribute('style', 'display:none;'); - cb(); - } else if (!cb) { - //if no options passed, show it works message - this.showStarterMessage(); - } + isValidLocale : function (locale) { + //matches 2 letter country code then optionally + //a dash or underscore followed by a country or language identifier + //i currently only allow a language identifier 2-3 chars long + + localeRegex = new RegExp('^[a-zA-Z][a-zA-Z]([-_][a-zA-z]{2,3})?$'); + return localeRegex.test(locale); + }, - return this; - }, - - //Display Apache-like "It works" message if no options - showStarterMessage : function (){ - var starterMsg = document.createElement('div'); - starterMsg.setAttribute('id', 'starterMsg'); - starterMsg.innerHTML = "<h1>It Works</h1>"; - document.body.appendChild(starterMsg); - }, - - statusUpdate : function ( current, error, total) { - this.statusDiv.innerHTML = "" + current + "/" + total + (error > 0 ? " [ "+error+" ]":''); - }, + normalizeLocale : function(locale) { + var lang = ""; + var country = ""; + var divider = ""; + + lang = locale.slice(0, 2).toLowerCase(); + divider = "_"; + country = locale.slice(3, 6).toUpperCase(); + + return locale.length > 2 ? "" + lang + divider + country : lang; + }, - - - isValidLocale : function (locale) { - //matches 2 letter country code then optionally - //a dash or underscore followed by a country or language identifier - //i currently only allow a language identifier 2-3 chars long - - localeRegex = new RegExp('^[a-zA-Z][a-zA-Z]([-_][a-zA-z]{2,3})?$'); - return localeRegex.test(locale); - }, - - normalizeLocale : function(locale) { - var lang = ""; - var country = ""; - var divider = ""; - - lang = locale.slice(0, 2).toLowerCase(); - divider = "_"; - country = locale.slice(3, 6).toUpperCase(); - return locale.length > 2 ? "" + lang + divider + country : lang; - }, - - computeLocalePath : function(locale) { - return "../assets/" + locale + "/"; - }, + }; - }; - - -Karma.kMedia = { - name : "", - fileName : "", - path : "", - localized : false, - type : "", - media : undefined, - - init : function () { + Karma.kMedia = { + name : "", + file : "", + path : "", + localized : false, + _type : "", + media : undefined, + + init : function (asset) { + Karma.KarmaRoot._counters.total++; + + if (asset.name === undefined || asset.file === undefined){ + throw new Error("properties name and file have to be defined"); + } else { + this.name = asset.name; + this.file = asset.file; + } + //_type is a private variable used internally + if (asset._type === undefined){ + throw new Error("the _type property must be set. " + + "Blame the karma library authors as this is an internal value"); + } else { + if (Karma.isValidType(asset._type)){ + this._type = asset._type; + switch ( this._type ) { + case "image": this.media = new Image(); + break; + case "sound": this.media = new Audio(); + break; + case "svg": + //this.media = new Audio(); + break; + default: throw new Error ("Media type not supported"); + } - } -}; - -Karma.checkLocalized = function (options) { - if (options.localized && typeof options.localized === "boolean" ) { - return localized; - } else { - throw new Error("This is not a valid value for the localized option"); - } -}; + } else { + throw new Error("the _type property supplied is invalid. " + + "Blame the karma library authors as this is an internal value"); + } + } + + if(Karma.isLocalized(asset.localized)){ + this.localized = asset.localized; + this.path = Karma.computeLocalePath(Karma.KarmaRoot.locale) + + this.type + "s/"; + } + + //IMPORTANT: This one magic line loads the file + this.media.src = this.src = this.path + this.file; + + //add event handlers + addEventHandlers(this); + + return this; + }, + addEventHandlers : function (kmedia) { + var elemKarma = document.getElementById('karma-loader'); + kmedia.media.addEventListener( + "load", + function (e) { + Karma.KarmaRoot._counters.loaded++; + Karma.KarmaRoot.updateMessage(); + kmedia.status = "loaded";}, false); + kmedia.media.addEventListener( + "error", + function (e) { + Karma.KarmaRoot._counters.errors++; + kmedia.status = "error"; + var errorMsg = "Error: " + kmedia._type.toUpperCase() + + " " + kmedia.name + " cannot be loaded."; + Karma.KarmaRoot.updateMessage(errorMsg); + }, + false); + kmedia.media.addEventListener( + "abort", + function (e) { + kmedia.status = "aborted"; + var errorMsg = "ABORT: " + kmedia._type.toUpperCase() + + " " + kmedia.name + " loading was aborted."; + Karma.KarmaRoot.updateMessage(errorMsg); + + }, false); + + }, + }; + + Karma.isValidType = function (type){ + var regex = new RegExp('^(image||svg||sound||video||surface)$'); + return regex.test(type); + }; + + Karma.isLocalized = function (boolLocalized) { + if (typeof boolLocalized === "boolean" ) { + if(boolLocalized === true && + Karma.KarmaRoot.locale === undefined){ + throw new Error("You cannot localize a media asset" + + " if the global locale for Karma isn't set"); + } else { + return true; + } + } else if (typeof boolLocalized === undefined){ + return false; + } else{ + throw new Error("This is not a valid value for the localized option"); + } + }; + Karma.computeLocalePath = function(locale) { + return "../assets/" + locale + "/"; + }; diff --git a/tests/tests.js b/tests/tests.js index 6e03d5d..252b49c 100755 --- a/tests/tests.js +++ b/tests/tests.js @@ -222,8 +222,6 @@ "Doesn't screw up locale that is already ok"); ok (Karma.karma.normalizeLocale("en") === "en", "handles 2 letter locale."); - console.log(Karma.karma.normalizeLocale("en")); - }); @@ -245,7 +243,7 @@ test("Karma.kMedia", function (){ - + }); @@ -278,12 +276,12 @@ ok(shouldError( function(){ kMock.init({}); - }), "Throw error if type, name, or file not specified"); + }), "Throw error if _type, name, or file not specified"); var kMedia1 = Karma.create(Karma.karma.kMedia); var oldErrors = Karma.karma._counters.errors; var oldTotal = Karma.karma._counters.total; - kMedia1.init({name: "notthere", type : "image", + kMedia1.init({name: "notthere", _type : "image", file: "notthere.png"}); ok(kMedia1.status === "error", "bad file name produces error"); ok(Karma.karma._counters.errors === oldErrors + 1 , @@ -296,7 +294,7 @@ oldErrors = Karma.karma._counters.errors; oldTotal = Karma.karma._counters.total; - kMock = { name: "chimp", type: "image", file: "happyMonkey"}; + kMock = { name: "chimp", _type: "image", file: "happyMonkey"}; kMedia1 = Karma.create(Karma.karma.kMedia).init(kMock); ok(kMedia1.status === "loaded", "Good file is loaded"); ok(Karma.karma.counterrors === oldErrors, @@ -309,7 +307,7 @@ ok(shouldError( function () { kMock.init({ name: 'esMonkey', file: 'HappyMonkey.jpg', - type: 'image', localized: true }); + _type: 'image', localized: true }); }), "You can't localize an asset if the locale isn't defined for your lesson"); @@ -318,7 +316,7 @@ oldErrors = Karma.karma._counters.errors; oldTotal = Karma.karma._counters.total; kMock.init({ name : 'trigger', file : 'trigger.ogg', - type : "sound", localized : true}); + _type : "sound", localized : true}); ok(kMock.status === "error", "Asset has status properly set to error"); ok(Karma.karma._counters.errors === oldErrors + 1, "Loading a localized file emits an error event if a localized version doesn't exist"); @@ -329,7 +327,7 @@ oldErrors = Karma.karma._counters.errors; oldTotal = Karma.karma._counters.total; kMock.init({ name : 'monkey', file : 'happyMonkey.jpg', - type : "image", localized : true}); + _type : "image", localized : true}); ok(Karma.karma._counters.errors === oldErrors, "Properly loads localized file"); ok(Karma.karma._counters.total === oldTotal + 1 , @@ -338,7 +336,7 @@ - test("Karma.karma.checkLocalized", + test("Karma.isLocalized(boolLocalized)", function(){ /* * reject non-boolean values @@ -346,15 +344,26 @@ * produce error if item is localized but not * locale isn't set for karma object */ - - ok(Karma.karma.checkLocalized(true), + Karma.KarmaRoot.locale = "en"; + ok(Karma.isLocalized(true), "handles true string value"); - ok(Karma.karma.checkLocalized(false), + ok(Karma.isLocalized(false), "handles false string value"); ok(shouldError(function(){ - Karma.karma.checkLocalized("true");}), + Karma.isLocalized("true");}), "rejects non-boolean value"); - + + if (Karma.KarmaRoot === undefined) { + Karma.KarmaRoot = {}; + } + + Karma.KarmaRoot.locale = undefined; + + ok(shouldError(function(){ + Karma.isLocalized(true); + }), + "Emits error if item is localized but Karma instance isn't"); + }); |