From 15721c5a271c26ac755819dee67f6ae947ff8f89 Mon Sep 17 00:00:00 2001 From: Peter Date: Tue, 02 Mar 2010 20:40:13 +0000 Subject: Merge commit 'bryan/master' --- (limited to 'examples/lessons/6_Maths_multiplyingFractions/js/karma.js') diff --git a/examples/lessons/6_Maths_multiplyingFractions/js/karma.js b/examples/lessons/6_Maths_multiplyingFractions/js/karma.js new file mode 100755 index 0000000..03f027b --- /dev/null +++ b/examples/lessons/6_Maths_multiplyingFractions/js/karma.js @@ -0,0 +1,1018 @@ +/* Documentation Note: + * Public methods and properties are commented with /** some text *\/ + * and private methods and properties are commented with // + * + * Please leave it that way to keep this documentation sane + */ + + +/* +* Karma Framework +* http://karmaeducation.org +* +* Copyright (c) 2009 +* Bryan W Berry bryan@olenepal.org +* Felipe López Toledo zer.subzero@gmail.com +* +* Under MIT License: +* Permission is hereby granted, free of charge, to any person +* obtaining a copy of this software and associated documentation +* files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, +* copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following +* conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +* OTHER DEALINGS IN THE SOFTWARE. +*/ + +/** +* @fileOverview Contains karma library +* @author Bryan Berry +* @author Felipe Lopez Toledo +*/ + + +//common.js modules use exports object +if(!this.exports) { + exports = {}; +} + + + +/** Checks if the current document type is set to HTML 5, throws + * an error otherwise, then initializes the karma object and returns + * a reference to that object. + * @namespace Global namespace for Karma library + * @param {Object} [options={}] options for intializing Karma library + * @param {String} [options.locale=''] sets current locale Not Yet Implemented + * @param {Array} [options.image=[]] array of images to be converted into a collection + * @param {Array} [options.audio=[]] array of audio to be converted into a collection + * @param {Array} [options.video=[]] array of videos to be converted into a collection + * @param {Array} [options.svg=[]] array of SVG elements to be + * converted into a collection. Each SVG element must already exist in the html document + * @param {Array} [options.canvas=[]] array of canvas elements + * to be converted into a collection. Each canvas element must already exist in the + * html document and width and height of each element must be set as attributes + * @throws {Error} if the document type declaration is not set to HTML 5, e.g. + * + * @throws {Error} If any of the initialization parameters are invalid values + * @returns {Object} Karma.karma -- reference to the initialized Karma library + * @example + * + * var k = Karma({ + * image: [ + * {name: "ninja", file: "ninja.png"}, + * {name: "cowboy", file: "cowboy.png"} + * ], + * audio: [ + * {name: "woosh", file: "woosh.ogg"}, + * {name: "yeehaw", file: "yeehaw.ogg"} + * ], + * video: [ + * {name: "attack", file: "attack.ogv"}, + * {name: "ride", file: "ride.ogv"} + * ] + * canvas: [ + * {name: "ninja", domId: "ninjaCanvas"}, + * {name: "cowboy", domId: "cowboyCanvas"} + * ], + * svg: [ + * {name: "ninja", domId: "ninjaSvg"}, + * {name: "cowboy", domId: "cowboySvg"} + * ], + * }); + * Next, call the ready function with a callback to your program code + * + * k.ready(function () { ... your application code . . . } + * + * after that you can access each asset like so + * k.image.ninja; + * k.svg.cowboy; + * k.audio.yeehaw.play(); + * k.canvas.ninja.drawImage(k.image.ninja, 0, 0); + * + */ +var Karma = exports.Karma = function (options) { + Karma._isHtml5(document.doctype.nodeName); + + if ( Karma.karma._initialized === true ) { + return Karma.karma; + } else { + return Karma.karma._init(options); + } +}; + + +//helper functions + +/**This emulates the Object.create method in ecmascript 5 spec + * This isn't a full implementation as it doesn't support + * This has the same functionality as Crockford's beget method + * and this primary building block for prototypal inheritance in + * this library + * @param {Object} target that the new object's prototype should point to + * @returns {Object} object whose prototype points to target + * @example + * + * var ninja = { weapon : "sword" }; + * var ninja1 = Karma.create(ninja); + * ninja1.sword === "sword" + */ +Karma.create = function (target){ + function F () {}; + F.prototype = target; + return new F(); +}; + +/** Returns a shallow copy of the passed in object + * @param {Object} target to be copied + * @returns {Object} a shallow copy of target + */ +Karma.clone = function (target){ + var copy = {}; + for ( var i in target ) { + if(target.hasOwnProperty(i)){ + copy[i] = target[i]; + } + } + return copy; +}; + +/** Extends properties of the target object with those of + * the source object + * @param {Object} target object to be extended + * @param {Object} source whose properties will extend target + * @returns {Object} target extended by source + */ +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 + * @param {Object} parent1 will be prototype of returned object + * @param {Object} parent2 will extend properties of returned object + * @returns {Object} object that whose prototype is parent1 and has + * been extended with properties of parent2 + */ +Karma.copyObjectPlus = function (parent1, parent2){ + function F () {}; + F.prototype = parent1; + var G = new F(); + return Karma.objectPlus(G, parent2); +}; + +//Enables function chaining for a specified list of function names +//IMPORTANT: use of closures here with "this" and "that" is __very__ +//complicated here +Karma._makeChain = function (chainingFunctions) { + var that = this; + var _chainFunction = function ( name ){ + that[ name ] = function ( ){ + var type = typeof this.ctx[name]; + if ( type === "function") { + this.ctx[ name ].apply( this.ctx, arguments ); + }else if ( type === "string" ){ + this.ctx[ name ] = arguments[0]; + }else { + throw ("wtf?!: impossible to chain " + name + "!"); + } + return this; + }; + }; + + for (var i = 0; i < chainingFunctions.length; i++){ + _chainFunction( chainingFunctions[ i ] ); + } +}; + +//Throws big ugly error if doctype isn't html5 +Karma._isHtml5 = function (doctype){ + var regex = new RegExp('^html$', 'i'); + if(!regex.test(doctype)){ + var errorMsg = "ERROR: The doctype must be set to " + + "in order to use Karma. Karma require you use html5"; + var errorElem = document.createElement('div'); + errorElem.setAttribute('id', 'errorDoctype'); + errorElem.innerText = errorMsg; + document.body.appendChild(errorElem); + throw new Error(errorMsg); + } +}; + + +/** Stores global settings for the Karma library + * @class This object stores the global settings for the Karma library + */ +Karma.karma = { + /** This is the global locale as passed to Karma(), + * such as "en", "es_SP" + * @type string + * @default undefined + */ + locale : undefined, + /** Collection of images with special helper + * methods added to each reference + * @type object + * @default empty object + */ + image : {}, + /** Collection of audio with special helper + * methods added to each reference + * @type object + * @default empty object + */ + audio : {}, + /** Collection of canvas with special helper + * methods added to each reference + * @type object + * @default empty object + */ + canvas : {}, + /** Collection of svgs with special helper + * methods added to each reference + * @type object + * @default empty object + */ + svg : {}, + /** Collection of videos with special helper + * methods added to each reference + * @type object + * @default empty object + */ + video : {}, + _localized : false, + _assetPath : "assets/", + _localePath : "", + _initialized : false, + _statusDiv: undefined, + _loaderDiv : undefined, + _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._initialized = true; + + //set up message that show count of assets loaded + //and has an ordered list to append error messages to + var _statusDiv = this._statusDiv = document.createElement('div'); + this._loaderDiv = this._loaderDiv = document.createElement('div'); + var errorList = document.createElement('ol'); + + _statusDiv.setAttribute('id', 'karma-status'); + _statusDiv.setAttribute('style', 'position:absolute;'); + _statusDiv.innerHTML = 'Karma is loading ...'; + this._loaderDiv.setAttribute('id', 'karma-loader'); + this._loaderDiv.setAttribute('class', 'status'); + errorList.setAttribute('id', 'errorList'); + + _statusDiv.appendChild(this._loaderDiv); + this._statusDiv.appendChild(errorList); + document.body.appendChild(_statusDiv); + + + //chain the functions for kCanvas and kSvg + Karma._makeChain.call(Karma.kCanvas, + Karma.kCanvas._chainingFunctions); + //Karma._makeChain.apply(Karma.kSvg, Karma.kSvg._chainingFunctions); + + + + //regular expression that matches the name of aprivate property + // the karma object + var regexPrivate = new RegExp('^_.*'); + + for ( var option in options ) { + if (options.hasOwnProperty(option)){ + if (option === "image" || option === "audio" || option === + "svg" || option === "video" || option === "canvas"){ + + 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; + } + + switch (option){ + case "locale": + + if (this._isValidLocale(options[option])){ + this.locale = this._normalizeLocale(options[option]); + this._localized = true; + this._localePath = Karma._computeLocalePath(this.locale); + } else { + throw new Error("locale provided to karma._init() is invalid"); + } + + break; + case "image": + options[option]._type = 'image'; + Karma._makeImages(options[option]); + break; + case "audio": + options[option]._type = 'audio'; + Karma._makeAudio(options[option]); + break; + case "video": + options[option]._type = 'video'; + Karma._makeVideos(options[option]); + break; + case "svg": + options[option]._type = 'svg'; + Karma._makeSvgs(options[option]); + break; + case "canvas": + options[option]._type = 'canvas'; + Karma._makeCanvases(options[option]); + break; + } + } + } + + + + return this; + }, + + /** Waits until all assets loaded, i.e. ready, then calls callback + * @param {Function} [cb] callback function + * @returns this + * @throws {Error} if Karma.karma is not initialized with the + * Karma({ options }) function + * @example + * + * var k = Karma({ . . . your assets here . . . }); + * k.ready(function(){ .. your code here . . .}); + * + * your code will not be called until all assets have been loaded + * + */ + ready : function( cb ) { + var that = this; + if (Karma.karma._initialized !== true){ + throw new Error("Karma.karma not initialized"); + } + + if (this._counters.loaded !== this._counters.total){ + setTimeout(function(){ that.ready(cb);}, 5); + } else if (cb) { + //hide the "Karma is loading..." message + this._statusDiv.setAttribute('style', 'display:none;'); + + cb(); + } else if (!cb) { + //hide the "Karma is loading..." message + this._statusDiv.setAttribute('style', 'display:none;'); + + //if no options passed, show it works message + this._showStarterMessage(); + } + + + + + return this; + }, + + //Display Apache-like "It works" message if no options + _showStarterMessage : function (){ + var starterMsg = document.createElement('div'); + starterMsg.setAttribute('id', 'starterMsg'); + starterMsg.innerHTML = "

It Works

"; + document.body.appendChild(starterMsg); + }, + + //Updates visible counter of how many assets are loaded + _updateStatus : function (errorMsg) { + var loaded = this._counters.loaded; + var total = this._counters.total; + var errors = this._counters.errors; + this._loaderDiv.innerHTML = "Loaded " + loaded + " / " + total + + "" + (errors > 0 ? " Errors [ " + errors +" ]" : ''); + if (errorMsg) { + var liError = document.createElement('li'); + liError.innerHTML = errorMsg; + var errorList = document.getElementById('errorList'); + errorList.appendChild(liError); + } + }, + + //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 + _isValidLocale : function (locale) { + var 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; + }, + + // Below are geometry and math helper methods + + /** + * Converts a value from degrees to radians. + * @param {Number} angle The angle in degrees + * @returns {Number} The angle in radians + */ + radians : function( angle ){ + return ( angle / 180 ) * Math.PI; + }, + /** + * Gets the square of the Euclidian (ordinary) distance between 2 points. + * @param {Object} Point No. 0 + * @param {Number} Point0.x + * @param {Number} Point0.y + * @param {Object} Point No. 1 + * @param {Number} Point1.x + * @param {Number} Point1.y + * @returns {Number} The square of the Euclidian distance + * @example + * + * p0 = {x:0, y:1}; + * p1 = {x:50, y:70}; + * var d = distance2(p0, p1); + * + */ + distance2 : function ( p0, p1 ) { + return (p1.x - p0.x) * (p1.x - p0.x) + (p1.y - p1.y) * (p1.y - p1.y); + }, + /** + * Gets the Euclidian (ordinary) distance between 2 points.
+ * Warning: It's slower than distance2 function + * @param {Object} Point No. 0 + * @param {Number} Point0.x + * @param {Number} Point0.y + * @param {Object} Point No. 1 + * @param {Number} Point1.x + * @param {Number} Point1.y + * @returns {Number} The Euclidian distance + * @example + * + * p0 = {x:0, y:1}; + * p1 = {x:50, y:70}; + * var d = distance2(p0, p1); + * + */ + distance : function ( p0, p1 ) { + return Math.sqrt( this.distance2( p0, p1 ) ); + }, + /** Returns a random number within the range provided + * @param {Number} lower limit of the range, lowest number that can be returned + * @param {Number} upper limit of the range, highest number that can be returned + * @returns {Number} number that is >= lower and <= upper + * @example + * + * var num = rand(0, 10); + * + * num could be 0, 1, 2, 3 ... or 10 + * + */ + rand : function ( lower, upper ){ + return Math.round( Math.random() * (upper - lower) + lower ); + } + +}; + +/** Prototypal object for images, videos, and audio files but + * does not include svg or canvas elements + * @class This object is the prototype for images, videos, and audio files but + * does not include svg or canvas elements + * @ throws {Error} if the individual asset is set to be localized but + * the globale locale is not set on the Karma.karma object + * @ throws {Error} if the name and file properties are not supplied + * @example + * kMedia is the prototype object for images, audio, and videos. + * These 'media' assets are loaded in a distinctly different way + * from the canvas or svg assets. They also have distinctly different + * helper methods + * + * You initialize the kMedia assets by passing an array of objects + */ +Karma.kMedia = { + /** file location of asset + * @type String + * @default "" + */ + file : "", + /** media object + * @type Audio|Image|Video + * @default undefined + */ + media : undefined, + //actual path to the file + _path : "", + //if using localized version of this asset + _localized : false, + //audio, image, or video + _type : "", + //initializes kMedia instance with values provided by user + _init : function (asset) { + asset._localized = asset._localized || false; + Karma.karma._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 "audio": this.media = new Audio(); + break; + //case "video": + //NYI + //this.media = new Video(); + break; + default: throw new Error("Media type not supported"); + } + + } 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.karma._localePath + + this._type + "/"; + } else { + this._path = Karma.karma._assetPath + + this._type + "/"; + } + + + //IMPORTANT: This one magic line loads the file + this.media.src = this.src = this._path + this.file; + + //add event handlers + this._addEventHandlers(); + + if (this._type === "audio"){ + this.media.autobuffer = true; + this.media.load(); + } + + + return this; + }, + //Adds event handlers to update the counters when + //the asset is successfully or unsuccessfully loaded + _addEventHandlers : function () { + var that = this; + var loadEvent = "load"; + //Browser Hack recommended by chromium devs + //http://code.google.com/p/chromium/issues/detail?id=20251&q=loading%20audio&colspec=ID%20Stars%20Pri%20Area%20Type%20Status%20Summary%20Modified%20Owner%20Mstone%20OS#c4 + if (this._type === "audio" || this._type === "video"){ + loadEvent = "canplaythrough"; + } + + that.media.addEventListener( + loadEvent, + function (e) { + Karma.karma._counters.loaded++; + Karma.karma._updateStatus(); + that.status = "loaded";}, false); + + that.media.addEventListener( + "error", + function (e) { + Karma.karma._counters.errors++; + that.status = "error"; + var errorMsg = "Error: " + that._type.toUpperCase() + + " " + that.name + " cannot be loaded."; + Karma.karma._updateStatus(errorMsg); + }, + false); + that.media.addEventListener( + "abort", + function (e) { + Karma.karma._counters.total++; + that.status = "aborted"; + var errorMsg = "ABORT: " + that._type.toUpperCase() + + " " + that.name + " loading was aborted."; + Karma.karma._updateStatus(errorMsg); + + }, false); + + } + +}; + +//determine if it is a valid type of asset +Karma._isValidType = function (type){ + return type === "image" || + type === "svg" || + type === "audio" || + type === "video" || + type === "canvas"; +}; + +Karma._isLocalized = function (boolLocalized) { + if (typeof boolLocalized === "boolean" ) { + if(boolLocalized === true && + Karma.karma.locale === undefined){ + throw new Error("You cannot localize a media asset" + + " if the global locale for Karma isn't set"); + } else { + return boolLocalized; + } + } 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 Karma.karma._assetPath + locale + "/"; +}; + +Karma._makeImages = function (imgConfigs){ + var makeImage = function (imgConfig){ + var image = undefined; + imgConfig._type = "image"; + image = Karma.create(Karma.kMedia)._init(imgConfig); + Karma.karma.image[imgConfig.name] = image; + }; + + imgConfigs.forEach(function(imgConfig){ makeImage(imgConfig);}); + +}; + +Karma._makeAudio = function (audioConfigs){ + var makeAudio = function (audioConfig){ + var audio = undefined; + audioConfig._type = "audio"; + audio = Karma.create(Karma.kMedia)._init(audioConfig); + audio.play = function () { + //hack to fix the audio "stuttering" problem + //more info: https://bugs.launchpad.net/karma/+bug/426108 + this.media.currentTime = 0.1; + this.media.play(); + }; + Karma.karma.audio[audioConfig.name] = audio; + }; + + audioConfigs.forEach(function(audioConfig){ makeAudio(audioConfig);}); + +}; + + +Karma._makeCanvases = function (canvasConfigs){ + var makeCanvas = function (canvasConfig){ + var canvas = undefined; + canvas = Karma.create(Karma.kCanvas)._init(canvasConfig); + Karma.karma.canvas[canvasConfig.name] = canvas; + }; + + canvasConfigs.forEach(function(canvasConfig){ makeCanvas(canvasConfig);}); + +}; + +/** Prototypal object for each canvas element submitted to Karma in the + * Karma() method + * @throws {Error} if the name and domId for the canvas element are not specified + * @thows {Error} if the supplied domId does not match an element in the DOM + * @class This object is the prototype for each canvas element submitted to Karma in the + * Karma() method + */ +Karma.kCanvas = { + /** Name of the canvas, used internally by karma.js + * @type String + * @default '' + */ + name : '', + /** Width of canvas element + * @type Number + * @default 0 + */ + width: 0, + /** Height of canvas element + * @type Number + * @default 0 + */ + height: 0, + /** Whether canvas is visible + * @type boolean + * @default true + */ + visible: true, + /** Element ID for canvas element in html document + * @type String + * @default undefined + */ + domId: undefined, + /** Reference to the DOM element + * @type DOMElement + * @default undefined + */ + node: undefined, + /** The 2 Dimensional Rendering context property for this canvas + * @type 2DRenderingContext + * @default undefined + */ + ctx: undefined, + /** Frames Per Second, I don't know what the purpose of this is, + * Felipe made it up + * @type Number + * @default 24 + */ + fps: 24, + + //initializes object with values provides by user + _init: function (config) { + for (var option in config){ + if (config.hasOwnProperty(option)){ + switch (option){ + case "name": + this.name = config[option]; + break; + case "domId": + this.domId = config[option]; + break; + case "width": + if(!this.height){ + throw new Error("If you specify a width you must also" + + "specify a height"); + } + this.width = config[option]; + break; + case "height": + if(!this.width){ + throw new Error("If you specify a height you must also" + + "specify a width"); + } + this.height = parseInt(config.option, 10); + break; + case "fps": + this.fps = parseInt(config.option, 10); + break; + } + } + } + + if(this.domId && document.getElementById(this.domId)){ + this.node = document.getElementById(this.domId); + this.ctx = this.node.getContext('2d'); + } else { + throw new Error('you must specify a valid domId that' + + 'is in your html page'); + } + + if(!config.height && !config.width){ + this.width = parseInt(this.node.getAttribute('width'), 10); + this.height = parseInt(this.node.getAttribute('height'), 10); + } + + return this; + }, + /** Clear area of canvas element specified by parameters, if no + * parameters supplied, clears entire canvas + * @param {Number} [x=0] x coordinate, defaults to zero if left blank + * @param {Number} [y=0] y coordinate, defaults to zero if left blank + * @param {Number} [width=0] width of area to be cleared, defaults + * entire width of canvas + * @param {Number} [height=0] height of area to be cleared, defaults + * entire height of canvas + * @returns this + * @example + * + * k.canvas.ninja.clear(); + * // clears the entire ninja canvas + * + * k.canvas.ninja.clear(0, 10, 20, 30); + * //clears a specific portion of the ninja canvas + * + */ + clear : function ( x, y, width, height ) { + var that = this; + that.ctx.clearRect( + x || 0, + y || 0, + width || that.width, + height || that.height + ); + return that; + }, + + //These are all properties or methods of the canvas element's + //2 dimensional context + _chainingFunctions : [ + "globalAlpha", "globalCompositeOperation", "lineWidth", "lineCap", + "lineJoin", "miterLimit", "font", "textAlign", "textBaseline", "save", + "restore", "scale", "rotate", "translate", "transform", "setTransform", + "clearRect", "fillRect", "strokeRect", "beginPath", "closePath", + "moveTo", "lineTo", "quadraticCurveTo", "bezierCurveTo", "arcTo", + "arc", "rect", "fill", "stroke", "clip", "fillText", "strokeText", + "measureText", "isPointInPath", "strokeStyle", "fillStyle", + "createLinearGradient", "createRadialGradient", "createPattern", + "shadowOffsetX", "shadowOffsetY", "shadowBlur", "shadowColor", + //"mozTextStyle", "mozDrawText", "mozMeasureText", "mozPathText", + "mozTextAlongPath", "drawImage", "getImageData", "putImageData", + "createImageData", "drawWindow" + ] +}; + + + +Karma._makeSvgs = function (svgConfigs){ + var makeSvg = function (svgConfig){ + var svg = undefined; + svg = Karma.create(Karma.kSvg)._init(svgConfig); + Karma.karma.svg[svgConfig.name] = svg; + }; + + svgConfigs.forEach(function(svgConfig){ makeSvg(svgConfig);}); + +}; + +/** Prototypal object for each svg element submitted to Karma in the + * Karma() method + * @throws {Error} if the name and domId for the svg element are not specified + * @thows {Error} if the supplied domId does not match an element in the DOM + * @class This object is the prototype for each svg element submitted to Karma in the + * Karma() method + */ +Karma.kSvg = { + /** name of instance, used internally + * @typeof string + * @default "" + */ + name : "", + /** width of element + * @type number + * @default 0 + */ + width: 0, + /** height of element + * @type number + * @default 0 + */ + height: 0, + /** Status of element, either "loaded" or "error" + * @type string + * @default "" + */ + status: "", + /** Whether canvas is visible + * @type boolean + * @default true + */ + visible: true, + /** Element ID for canvas element in html document + * @type String + * @default undefined + */ + domId: undefined, + /** Reference to the DOM element + * @type DOMElement + * @default undefined + */ + node: undefined, + /** Reference to the SVGDocument + * @type SVGDocument + * @default undefined + */ + doc: undefined, + /** Reference to the root element of the SVG Document + * @type DocumentElement + * @default undefined + */ + root: undefined, + _localized : undefined, + _chainingFunctions: [], + _init: function (config) { + Karma.karma._counters.total++; + + for (var option in config){ + if (config.hasOwnProperty(option)){ + switch (option){ + case "name": + this.name = config[option]; + break; + case "domId": + this.domId = config[option]; + break; + case "width": + if(!this.height){ + throw new Error("If you specify a width you must also" + + "specify a height"); + } + this.width = parseInt(config[option], 10); + break; + case "height": + if(!this.width){ + throw new Error("If you specify a height you must also" + + "specify a width"); + } + this.height = config[option]; + break; + case "fps": + this.fps = config[option]; + break; + } + } + } + + if(this.domId && document.getElementById(this.domId)){ + this.node = document.getElementById(this.domId); + } else { + throw new Error('you must specify a valid domId that' + + 'is in your html page'); + } + + if(!config.height && !config.width){ + this.width = parseInt(this.node.getAttribute('width'), 10); + this.height = parseInt(this.node.getAttribute('height'), 10); + } + + var that = this; + that._addEventHandlers(); + + return this; + + + }, + _addEventHandlers : function () { + var that = this; + that.doc = that.node.getSVGDocument(); + that.node.addEventListener( + "load", + function (e) { + that.doc = that.node.getSVGDocument(); + that.root = that.doc.documentElement; + Karma.karma._counters.loaded++; + Karma.karma._updateStatus(); + that.status = "loaded"; + }, false); + + that.node.addEventListener( + "error", + function (e) { + Karma.karma._counters.loaded--; + Karma.karma._counters.errors++; + that.status = "error"; + var errorMsg = "Error: " + that._type.toUpperCase() + + " " + that.name + " cannot be loaded."; + Karma.karma._updateStatus(errorMsg); + }, + false); + that.node.addEventListener( + "abort", + function (e) { + that.status = "aborted"; + var errorMsg = "ABORT: " + that._type.toUpperCase() + + " " + that.name + " loading was aborted."; + Karma.karma._updateStatus(errorMsg); + + }, false); + + } +}; + +Karma._makeVideos = function (video){ + +}; -- cgit v0.9.1