1 /* Documentation Note:
  2  *   Public methods and properties are commented with /** some text *\/
  3  *   and private methods and properties are commented with //
  4  *   
  5  *   Please leave it that way to keep this documentation sane
  6  */
  7 
  8 
  9 /*
 10 *	Karma Framework
 11 *	http://karmaeducation.org
 12 *	
 13 *	Copyright (c)  2009
 14 *	Bryan W Berry		bryan@olenepal.org
 15 * 	Felipe López Toledo	zer.subzero@gmail.com
 16 *      
 17 *	Under MIT License:
 18 *	Permission is hereby granted, free of charge, to any person
 19 *	obtaining a copy of this software and associated documentation
 20 *	files (the "Software"), to deal in the Software without
 21 *	restriction, including without limitation the rights to use,
 22 *	copy, modify, merge, publish, distribute, sublicense, and/or sell
 23 *	copies of the Software, and to permit persons to whom the
 24 *	Software is furnished to do so, subject to the following
 25 *	conditions:
 26 *	
 27 *	The above copyright notice and this permission notice shall be
 28 *	included in all copies or substantial portions of the Software.	
 29 * 
 30 *	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 31 *	EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 32 *	OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 33 *	NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 34 *	HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 35 *	WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 36 *	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 37 *	OTHER DEALINGS IN THE SOFTWARE.
 38 */
 39 
 40 /**
 41 * @fileOverview Contains karma library
 42 * @author Bryan Berry <bryan@olenepal.org> 
 43 * @author Felipe Lopez Toledo <zer.subzero@gmail.com>
 44 */
 45 
 46 
 47 //common.js modules use exports object
 48 if(!this.exports) {
 49     exports = {};
 50 }
 51 
 52 
 53 
 54 /** Karma is the namespace for the Karma library and Karma() is the constructor 
 55  * function for the Karma library object Karma. 
 56  * Karma() checks if the current document type is set to HTML 5, throws
 57  * an error if not. Otherwise, initializes the karma object and returns
 58  * a reference to that object.
 59  * @namespace Global namespace for Karma library
 60  * @constructor
 61  * @param {Object} [options={}] options for intializing Karma library
 62  * @param {String} [options.locale=''] sets current locale Not Yet Implemented
 63  * @param {Array} [options.image=[]] array of images to be converted into a collection
 64  * @param {Array} [options.audio=[]] array of audio to be converted into a collection
 65  * @param {Array} [options.video=[]] NYI array of videos to be converted into a collection
 66  * @param {Array} [options.svg=[]] array of SVG elements to be 
 67  * converted into a collection. Each SVG element must already exist in the html document
 68  * @param {Array} [options.canvas=[]] array of canvas elements 
 69  * to be converted into a collection. Each canvas element must already exist in the 
 70  * html document and width and height of each element must be set as attributes
 71  * @throws {Error} if the document type declaration is not set to HTML 5, e.g. 
 72  * <!DOCTYPE html>
 73  * @throws {Error} If any of the initialization parameters are invalid values
 74  * @returns {Object} Karma -- reference to the initialized Karma library
 75  * @example
 76  * 
 77  * var k = Karma({ 
 78  *                 image: [ 
 79  *                    {name: "ninja", file: "ninja.png"}, 
 80  *                    {name: "cowboy", file: "cowboy.png"}
 81  *                         ],
 82  *                 audio: [
 83  *                    {name: "woosh", file: "woosh.ogg"},
 84  *                    {name: "yeehaw", file: "yeehaw.ogg"}
 85  *                         ],
 86  *                 video: [  //Not Yet Implemented
 87  *                    {name: "attack", file: "attack.ogv"},
 88  *                    {name: "ride", file: "ride.ogv"}
 89  *                         ]
 90  *                 canvas: [
 91  *                    {name: "ninja", domId: "ninjaCanvas"},
 92  *                    {name: "cowboy", domId: "cowboyCanvas"}
 93  *                         ],
 94  *                 svg: [ 
 95  *                    {name: "ninja", domId: "ninjaSvg"},
 96  *                    {name: "cowboy", domId: "cowboySvg"}
 97  *                         ],
 98  *                 });
 99  * Next, call the ready function with a callback to your program code
100  * 
101  * k.ready(function () { ... your application code . . . }                       
102  * 
103  * after that you can access each asset like so
104  * k.image.ninja;
105  * k.svg.cowboy;
106  * k.audio.yeehaw.play();
107  * k.canvas.ninja.drawImage(k.image.ninja, 0, 0);
108  * 
109  */	
110 var Karma = exports.Karma  = function (options) {
111     Karma._isHtml5(document.doctype.nodeName);
112 
113     if ( Karma._initialized === true ) {
114 	return Karma;
115     } else {
116 	return Karma._init(options);
117     }
118 };
119 
120 
121 //helper functions
122 
123 /**This emulates the Object.create method in ecmascript 5 spec
124  * This isn't a full implementation as it doesn't support an all of Object.create's features
125  * This has the same functionality as Crockford's beget method
126  * and this primary building block for prototypal inheritance in
127  * this library
128  * @param {Object} parent that the new object's prototype should point to
129  * @returns {Object} a new object whose prototype is parent
130  * @example
131  * 
132  * var ninja = { weapon : "sword" };
133  * var ninja1 = Karma.create(ninja);
134  * ninja1.weapon === "sword"
135  */
136 Karma.create = function (parent){
137     function F () {};
138     F.prototype = parent;
139     return new F();
140 };
141 
142 /** Returns a shallow copy of the passed in object
143  * @param {Object} target to be copied
144  * @returns {Object} a shallow copy of target
145  */
146 Karma.clone = function (target){
147     var copy = {};
148     for ( var i in target ) {
149 	if(target.hasOwnProperty(i)){
150 	    copy[i] = target[i];
151 	}
152     }
153     return copy;
154 };
155 
156 /** Extends properties of the target object with those of 
157  * the source object
158  * @param {Object} target object to be extended 
159  * @param {Object} source whose properties will extend target
160  * @returns {Object} target extended by source
161  */
162 Karma.objectPlus = function (target, source){
163     for ( var i in source){
164 	if (source.hasOwnProperty(i)){
165 	    target[i] = source[i];
166 	}
167     }
168     return target;
169 };
170 
171 Karma.extend = Karma.objectPlus;
172 
173 /** Creates a new object that is a prototype of the first argument
174  * then extends it with the properties of the second argument
175  * @param {Object} parent1 will be prototype of returned object
176  * @param {Object} parent2 will extend properties of returned object
177  * @returns {Object} object that whose prototype is parent1 and has 
178  * been extended with properties of parent2
179  */ 
180 Karma.copyObjectPlus = function (parent1, parent2){
181     function F () {};
182     F.prototype = parent1;
183     var G = new F();
184     return Karma.objectPlus(G, parent2);
185 };
186 
187 
188 //Throws big ugly error if doctype isn't html5
189 Karma._isHtml5 = function (doctype){
190     var regex = new RegExp('^html$', 'i');
191     if(!regex.test(doctype)){
192 	var errorMsg =  "ERROR: The doctype must be set to <!DOCTYPE html> " +
193 	    "in order to use Karma. Karma require you use html5";
194 	var errorElem = document.createElement('div');
195 	errorElem.setAttribute('id', 'errorDoctype');
196 	errorElem.innerText = errorMsg;
197 	document.body.appendChild(errorElem);
198 	   throw new Error(errorMsg);
199 	}
200 };
201 
202 /**
203  * Shuffles an array of items randomly
204  * @param {Array} oldList of choices to be shuffled
205  * @returns {Array} newlist of choices randomly reordered 
206  */
207 Karma.shuffle = function (oldList) {
208     var newList = oldList.slice(0);
209     for (var i = newList.length - 1; i > 0; i -= 1) {
210         var j = Karma.rand(0, i);
211         var t = newList[i];
212         newList[i] = newList[j];
213         newList[j] = t;
214     }
215     return newList;
216 };
217 
218 
219 /**
220  * Converts a number to numerals in the specified locale. Currently only
221  * supports Nepali
222  * @param {Number} Number to be converted
223  * @param {locale} locale that number should be converted to
224  * @returns {String} Unicode string for localized numeral 
225  */
226 Karma.convertNumToLocale = function(num, locale){
227     locale = locale || Karma.locale;
228     //48 is the base for western numerals
229     var convertDigit = function(digit){
230 	
231 	var numBase = 48;
232 	var prefix = "u00";
233 	
234 	if (locale === "ne"){
235 	    prefix = "u0";
236 	    numBase = 2406;
237 	}
238 	
239 	return '\\' + prefix + 
240 	    (numBase + parseInt(digit)).toString(16);
241     };
242     
243     var charArray = num.toString().split("").map(convertDigit);
244     return eval('"' + charArray.join('') + '"');
245 };
246 
247 /**
248  * @name Karma._n
249  * @function
250  * @public
251  * Alias for Karma.convertNumToLocale. Converts a number to numerals to 
252  * Karma.locale or to specified locale. Currently only supports Nepali
253  * @param {Number} Number to be converted
254  * @param {locale} locale that number should be converted to
255  * @returns {String} Unicode string for localized numeral 
256  */
257 Karma._n = Karma.convertNumToLocale;
258 
259 /* Scales the dimensions of document.body to the innerHeight and innerWidth
260  * of the viewport, i.e. browser window, with a minor offset to the height to 
261  * make sure the scrollbars do not appear
262  */
263 Karma.scaleToViewport = function(){
264     var width = window.innerWidth;
265     var height = window.innerHeight;
266     
267     //hack to ensure scrollbars don't appear
268     if (height === 900){
269 	height = "" + 884 + "px";
270     } else {
271 	height = "" + (height - 13) + "px";
272     }
273     
274     document.body.style.width = "" + width + "px";
275     document.body.style.height = height;
276 };
277 
278     // Below are geometry and math helper methods
279     
280 /**
281  * Converts a value from degrees to radians.
282  * @param {Number} angle The angle in degrees
283  * @returns {Number} The angle in radians 
284  */
285 Karma.radians = function( angle ){
286 	return ( angle / 180 ) * Math.PI;
287 };
288 
289 /**
290  *  Gets the square of the Euclidian (ordinary) distance between 2 points.
291  * @param {Object} Point No. 0
292  * @param {Number} Point0.x
293  * @param {Number} Point0.y
294  * @param {Object} Point No. 1
295  * @param {Number} Point1.x
296  * @param {Number} Point1.y
297  * @returns {Number} The square of the Euclidian distance 
298  * @example
299  * 
300  * p0 = {x:0, y:1};
301  * p1 = {x:50, y:70};
302  * var d = distance2(p0, p1);
303  * 
304  */
305 Karma.distance2 = function ( p0, p1 ) {
306     return   (p1.x - p0.x) * (p1.x - p0.x) + (p1.y - p1.y) * (p1.y - p1.y); 
307 };
308 
309 /**
310  * Gets the Euclidian (ordinary) distance between 2 points.<br>
311  * <b>Warning:</b> It's slower than distance2 function
312  * @param {Object} Point No. 0
313  * @param {Number} Point0.x
314  * @param {Number} Point0.y
315  * @param {Object} Point No. 1
316  * @param {Number} Point1.x
317  * @param {Number} Point1.y
318  * @returns {Number} The Euclidian distance 
319  * @example
320  * 
321  * p0 = {x:0, y:1};
322  * p1 = {x:50, y:70};
323  * var d = distance2(p0, p1);
324  * 
325  */
326 Karma.distance = function ( p0, p1 ) {
327 	return   Math.sqrt( this.distance2( p0, p1 ) ); 
328 };
329 
330 /** Returns a random number within the range provided
331  * @param {Number} lower limit of the range, lowest number that can be returned
332  * @param {Number} upper limit of the range, highest number that can be returned
333  * @returns {Number} number that is >= lower and <= upper
334  * @example
335  * 
336  * var num = rand(0, 10);
337  * 
338  * //num could be 0, 1, 2, 3 ... or 10
339  * 
340  */
341 Karma.rand = function ( lower, upper ){
342   return Math.floor(Math.random() * (upper - lower + 1) + lower);  
343 };
344 
345 
346 Karma.extend(Karma, {      
347     /** This is the global locale as passed to Karma(),
348      * such as "en", "es_SP"
349      * @fieldOf Karma
350      * @property {string} locale This is the global locale as passed to Karma()
351      * @default 'en'
352      */
353     locale : 'en',
354     /** Collection of images with special helper
355      * methods added to each reference
356      * @fieldOf Karma
357      * @type object
358      * @default empty object
359      */
360     image : {},
361     /** Collection of audio files with special helper
362      * methods added to each reference
363      * @fieldOf Karma
364      * @type object
365      * @default empty object
366      */
367     audio : {},
368     /** Collection of html 5 canvases with special helper
369      * methods added to each reference
370      * @fieldOf Karma
371      * @type object
372      * @default empty object
373      */
374     canvas : {},
375     /** Collection of svgs with special helper
376      * methods added to each reference
377      * @fieldOf Karma
378      * @type object
379      * @default empty object
380      */
381     svg : {},
382     /** Collection of videos with special helper
383      * methods added to each reference
384      * @fieldOf Karma
385      * @type object
386      * @default empty object
387      */
388     video : {},
389     _localized : false,
390     _assetPath : "assets/",
391     _localePath : "",
392     _initialized : false,
393     _statusDiv: undefined,
394     _loaderDiv : undefined,
395     _counters : { total : 0, errors : 0, loaded : 0},
396 
397     //This constructs the Karma object per values provided by the user
398     _init: function(options) {
399 	this._initialized = true;
400 	
401 	//set up message that show count of assets loaded
402 	//and has an ordered list to append error messages to
403 	var _statusDiv = this._statusDiv = document.createElement('div');
404 	this._loaderDiv = this._loaderDiv = document.createElement('div');	
405 	var errorList = document.createElement('ol');
406 
407 	_statusDiv.setAttribute('id', 'karma-status');
408 	_statusDiv.setAttribute('style', 'position:absolute;');
409 	_statusDiv.innerHTML = 'Karma is loading ...';
410 	this._loaderDiv.setAttribute('id', 'karma-loader');
411 	this._loaderDiv.setAttribute('class', 'status');
412 	errorList.setAttribute('id', 'errorList');
413 
414 	_statusDiv.appendChild(this._loaderDiv);
415 	this._statusDiv.appendChild(errorList);
416 	document.body.appendChild(_statusDiv);
417 
418 	//regular expression that matches the name of aprivate property
419 	// the karma object
420 	var regexPrivate = new RegExp('^_.*');
421 	
422 	for ( var option in options ) {
423 	    if (options.hasOwnProperty(option)){
424 		if (option === "image" || option === "audio" || option === 
425 		    "svg" || option === "video" || option === "canvas"){ 
426 		    
427 		    if(!(options[option] instanceof Array)){
428 			throw new Error("" + option + " must be an array");
429 		    } else if (options[option].length === 0){
430 			continue;
431 		    }
432 		} else if (regexPrivate.test(option)){
433 		    //don't overwrite a private property of karma object
434 		    continue;
435 		}
436 		
437 		switch (option){
438 		case "locale":
439 
440 		    if (this._isValidLocale(options[option])){
441 			this.locale = this._normalizeLocale(options[option]);
442 			this._localized = true;
443 			this._localePath = Karma._computeLocalePath(this.locale);
444 		    } else {
445 			throw new Error("locale provided to karma._init() is invalid");
446 		    }
447 		    
448 		    break;
449 		case "image":
450 		    options[option]._type = 'image';
451 		    Karma._makeCollection(options[option], 'image');
452 		    break;
453 		case "audio":
454 		    options[option]._type = 'audio';
455 		    Karma._makeCollection(options[option], 'audio');
456 		    break;
457 		case "video":
458 		    options[option]._type = 'video';
459 		    Karma._makeCollection(options[option], 'video');
460 		    break;
461 		case "svg":
462 		    options[option]._type = 'svg';
463 		    Karma._makeCollection(options[option], 'svg');
464 		    break;
465 		case "canvas":
466 		    options[option]._type = 'canvas';
467 		    Karma._makeCollection(options[option], 'canvas');
468 		    break;
469 		}
470 	    }
471 	}
472 
473 
474 
475 	return this;
476     },
477     
478     /** Waits until all assets loaded(ready), then calls callback cb
479      * @memberOf Karma
480      * @param {Function} [cb] callback function
481      * @returns this
482      * @throws {Error} if Karma is not initialized with the 
483      * Karma({ options }) function
484      * @example
485      * 
486      * var k = Karma({ . . . your assets here . . . });
487      * k.ready(function(){ .. your code here . . .});
488      * 
489      * your code will not be called until all assets have been loaded
490      * into collections
491      * 
492      */
493     ready : function( cb ) {
494 	var that = this;
495 	if (Karma._initialized !== true){
496 	    throw new Error("Karma not initialized");
497 	}
498 
499 	if (this._counters.loaded !== this._counters.total){
500 	    setTimeout(function(){ that.ready(cb);}, 5);
501 	} else if (cb) {
502 	    //hide the "Karma is loading..." message
503 	    this._statusDiv.setAttribute('style', 'display:none;');
504 
505 	     cb();
506 	} else if (!cb) {
507 	    //hide the "Karma is loading..." message
508 	    this._statusDiv.setAttribute('style', 'display:none;');
509 	    
510 	    //if no options passed, show it works message
511 	    this._showStarterMessage();
512 	}
513 	
514 	
515 	   
516 
517 	return this;
518     },
519 
520     //Display Apache-like "It works" message if no options
521     _showStarterMessage : function (){
522 	var starterMsg = document.createElement('div');
523 	starterMsg.setAttribute('id', 'starterMsg');
524 	starterMsg.innerHTML = "<h1>It Works</h1>";
525 	document.body.appendChild(starterMsg);
526     },
527 
528     //Updates visible counter of how many assets are loaded
529     _updateStatus : function (errorMsg) {
530 	var loaded = this._counters.loaded;
531 	var total = this._counters.total;
532 	var errors = this._counters.errors;
533 	this._loaderDiv.innerHTML = "Loaded " + loaded + " / " + total + 
534 	    "" + (errors > 0 ? " Errors [ " + errors +" ]" : '');
535 	if (errorMsg) {
536 	    var liError = document.createElement('li');
537 	    liError.innerHTML = errorMsg;
538 	    var errorList = document.getElementById('errorList');
539 	    errorList.appendChild(liError);  
540 	}
541     },	    
542 
543     //matches 2 letter country code then optionally
544     //a dash or underscore followed by a country or language identifier
545     //i currently only allow a language identifier 2-3 chars long
546     _isValidLocale : function (locale) {
547 	var localeRegex = new RegExp('^[a-zA-Z][a-zA-Z]([-_][a-zA-z]{2,3})?$');
548 	return localeRegex.test(locale);
549     },
550 
551     _normalizeLocale : function(locale) {
552 	var lang = "";
553 	var country = "";
554 	var divider = "";
555 
556 	lang = locale.slice(0, 2).toLowerCase();
557 	divider = "_";
558 	country = locale.slice(3, 6).toUpperCase();
559 	
560 	return locale.length > 2 ? "" + lang + divider + country : lang;
561     },
562     
563 
564     
565 });
566 
567 //Helper functions for creating assets
568 Karma._isLocalized = function (boolLocalized) {
569     if (typeof boolLocalized === "boolean" ) {
570 	if(boolLocalized === true && 
571 	   Karma.locale === undefined){
572 	    throw new Error("You cannot localize a media asset" +
573 			    " if the global locale for Karma isn't set");
574 	} else {
575 	    return boolLocalized;
576 	}
577     } else if (typeof boolLocalized === undefined){
578 	return false;
579     } else{ 
580 	throw new Error("This is not a valid value for the localized option");
581     }
582 };
583 
584 Karma._computeLocalePath = function(locale) {
585     return Karma._assetPath + locale + "/";
586 };
587 
588 
589 
590 
591 Karma._makeCollection = function (configs, type){
592     var makeAsset = function (config){
593 	var asset = undefined;
594 	var target = undefined;
595 	switch(type){
596 	    case "image":
597 		target = Karma.kImage;
598 		break;
599 	    case "audio":
600 		target = Karma.kAudio;
601 		break;
602 	    case "video":
603 		target = Karma.kVideo;
604 		break;
605 	    case "svg":
606 		target = Karma.kSvg;
607 		break;
608 	    case "canvas":
609 		target = Karma.kCanvas;
610 		break;
611 	}
612 
613 	asset = Karma.create(target)._init(config);
614 	Karma[type][config.name] = asset;
615     };
616 		       
617     configs.forEach(function(config){ makeAsset(config);});
618 };
619 
620 
621 
622 
623 
624 //Prototype objects for assets
625 
626 
627 /** Prototype object for images
628  *  @class This object is the prototype for images submitted to Karma in the
629  *  Karma() method
630  *  @ throws {Error} if the image asset is set to be localized but 
631  *  the global locale is not set on the Karma object
632  *  @ throws {Error} if the name and file properties are not supplied
633  *  @example
634  *  kImage is the prototype object for images. This 'media' asset is loaded 
635  *  in a distinctly different way from the canvas or svg assets.    
636  *
637  */
638 Karma.kImage = 
639     {
640     /** file location of image
641      * @type String
642      * @default ""
643      */
644     file : "",
645     /** media object
646      * @type Image
647      * @default undefined 
648      */	
649     media : undefined,
650     //actual path to the file
651     _path : "",
652     //if using localized version of this image
653     _localized : false,
654     _type : "image", 
655     //initializes kImage instance with values provided by user
656     _init : function (image) {
657 	image._localized = image._localized || false;
658 	Karma._counters.total++;
659 
660 	if (image.name === undefined || image.file === undefined){
661 	    throw new Error("properties name and file have to be defined");
662 	} else {
663 	    this.name = image.name;
664 	    this.file = image.file;
665 	}
666 
667 	this.media = new Image(); 
668 	
669 	if(Karma._isLocalized(image._localized)){
670 	    this._localized = image._localized;
671 	    this._path = Karma._localePath + "image/";
672 	} else {
673 	    this._path = Karma._assetPath + "image/";
674 	}
675 
676 	//IMPORTANT: This one magic line loads the file
677 	this.media.src = this.src = this._path + this.file;
678 	
679 	//add event handlers
680 	this._addEventHandlers();
681 
682 	
683 	return this;
684     },
685     //Adds event handlers to update the counters when 
686     //the image is successfully or unsuccessfully loaded
687     _addEventHandlers : function () {
688 	var that = this;
689 
690 	that.media.addEventListener(
691 	    "load", 
692 	    function (e) { 
693 		Karma._counters.loaded++;
694 		Karma._updateStatus();
695 		that.status = "loaded";}, false);
696 	
697 	that.media.addEventListener(
698 	    "error", 
699 	    function (e) { 
700 		Karma._counters.errors++;
701 		that.status = "error";
702 		var errorMsg = "Error: " + that._type.toUpperCase() +
703 		    " " + that.name + " cannot be loaded."; 
704 		Karma._updateStatus(errorMsg);
705 	    }, 
706 	    false);
707 	that.media.addEventListener(
708 	    "abort", 
709 	    function (e) { 
710 		Karma._counters.total++;
711 		that.status = "aborted";
712 		var errorMsg = "ABORT: " + that._type.toUpperCase() +
713 		    " " + that.name + " loading was aborted."; 
714 		Karma._updateStatus(errorMsg);
715 
716 	    }, false);
717     }
718     
719 };
720 
721 /** Prototype object for audio files 
722  *  @class This object is the prototype for audio files submitted to Karma in the
723  * Karma() method
724  *  @ throws {Error} if the individual audio asset is set to be localized but 
725  *  the globale locale is not set on the Karma object
726  *  @ throws {Error} if the name and file properties are not supplied
727  *  @example
728  *  kAudio is the prototype object for audio
729  *  The audio assets are loaded in a distinctly different way
730  *  from the canvas or svg assets. They also have distinctly different
731  *  helper methods 
732  *  
733  *  You initialize the kAudio assets by passing an array of objects
734  */
735 Karma.kAudio = {
736     /** file location of asset
737      * @type String
738      * @default ""
739      */
740     file : "",
741     /**  Media object. You can access the src, autobuffer, autoplay, loop, and 
742      * controls attributes 
743      * via the media property of kAudio. Read more about the properties of the <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#media-element-attributes">HTML 5 media element</a>
744      * @type Audio
745      * @default undefined
746      */	
747     media : undefined,
748     //actual path to the file
749     _path : "",
750     //if using localized version of this asset
751     _localized : false,
752     _type : "audio", 
753     //initializes kAudio instance with values provided by user
754     _init : function (audio) {
755 	audio._localized = audio._localized || false;
756 	Karma._counters.total++;
757 
758 	if (audio.name === undefined || audio.file === undefined){
759 	    throw new Error("properties name and file have to be defined");
760 	} else {
761 	    this.name = audio.name;
762 	    this.file = audio.file;
763 	}
764 
765 	this.media = new Audio(); 
766 	
767 	if(Karma._isLocalized(audio._localized)){
768 	    this._localized = audio._localized;
769 	    this._path = Karma._localePath  + "audio/";
770 	} else {
771 	    this._path = Karma._assetPath + "audio/";
772 	}
773 
774 
775 	//IMPORTANT: This one magic line loads the file
776 	this.media.src = this.src = this._path + this.file;
777 	
778 	//add event handlers
779 	this._addEventHandlers();
780 
781 	this.media.autobuffer = true;
782 	this.media.load();
783 
784 	
785 	return this;
786     },
787     //Adds event handlers to update the counters when 
788     //the asset is successfully or unsuccessfully loaded
789     _addEventHandlers : function () {
790 	var that = this;
791 	//'canplaythrough' event is a Browser Hack recommended by chromium devs
792 	//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
793 
794 	that.media.addEventListener(
795 	    "canplaythrough", 
796 	    function (e) { 
797 		Karma._counters.loaded++;
798 		Karma._updateStatus();
799 		that.status = "loaded";}, false);
800 	
801 	that.media.addEventListener(
802 	    "error", 
803 	    function (e) { 
804 		Karma._counters.errors++;
805 		that.status = "error";
806 		var errorMsg = "Error: " + that._type.toUpperCase() +
807 		    " " + that.name + " cannot be loaded."; 
808 		Karma._updateStatus(errorMsg);
809 	    }, 
810 	    false);
811 	that.media.addEventListener(
812 	    "abort", 
813 	    function (e) { 
814 		Karma._counters.total++;
815 		that.status = "aborted";
816 		var errorMsg = "ABORT: " + that._type.toUpperCase() +
817 		    " " + that.name + " loading was aborted."; 
818 		Karma._updateStatus(errorMsg);
819 
820 	    }, false);
821 
822     },
823     /** Plays the audio file  */
824     play : function () {
825 	    this.media.play();  
826     }
827     
828 };
829 
830 /** NYI:Prototype object for Video files 
831  *  @class Not Yet Implemented:This object is the prototype for video files submitted 
832  * to Karma in the Karma() method
833  *  @ throws {Error} if the individual video asset is set to be localized but 
834  *  the globale locale is not set on the Karma object
835  *  @ throws {Error} if the name and file properties are not supplied
836  */
837 Karma.kVideo = {
838     /** file location of asset
839      * @type String
840      * @default ""
841      */
842     file : "",
843     /** media object
844      * @type Video
845      * @default undefined 
846      */	
847     media : undefined,
848     //actual path to the file
849     _path : "",
850     //if using localized version of this asset
851     _localized : false,
852     _type : "video", 
853     //initializes kVideo instance with values provided by user
854     _init : function (video) {
855 	//Not Yet Implemented
856 	Karma._counters.errors++;
857 	throw new Error("Video is not Yet Implemented");
858 
859 	video._localized = video._localized || false;
860 	Karma._counters.total++;
861 
862 	if (video.name === undefined || video.file === undefined){
863 	    throw new Error("properties name and file have to be defined");
864 	} else {
865 	    this.name = video.name;
866 	    this.file = video.file;
867 	}
868 
869 	this.media = new Video(); 
870 	
871 	if(Karma._isLocalized(video._localized)){
872 	    this._localized = video._localized;
873 	    this._path = Karma._localePath  + "video/";
874 	} else {
875 	    this._path = Karma._assetPath + "video/";
876 	}
877 
878 
879 	//IMPORTANT: This one magic line loads the file
880 	this.media.src = this.src = this._path + this.file;
881 	
882 	//add event handlers
883 	this._addEventHandlers();
884 
885 	return this;
886     },
887     //Adds event handlers to update the counters when 
888     //the asset is successfully or unsuccessfully loaded
889     _addEventHandlers : function () {
890 	var that = this;
891 	//'canplaythrough' event is a Browser Hack recommended by chromium devs
892 	//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
893 
894 	that.media.addEventListener(
895 	    "canplaythrough", 
896 	    function (e) { 
897 		Karma._counters.loaded++;
898 		Karma._updateStatus();
899 		that.status = "loaded";}, false);
900 	
901 	that.media.addEventListener(
902 	    "error", 
903 	    function (e) { 
904 		Karma._counters.errors++;
905 		that.status = "error";
906 		var errorMsg = "Error: " + that._type.toUpperCase() +
907 		    " " + that.name + " cannot be loaded."; 
908 		Karma._updateStatus(errorMsg);
909 	    }, 
910 	    false);
911 	that.media.addEventListener(
912 	    "abort", 
913 	    function (e) { 
914 		Karma._counters.total++;
915 		that.status = "aborted";
916 		var errorMsg = "ABORT: " + that._type.toUpperCase() +
917 		    " " + that.name + " loading was aborted."; 
918 		Karma._updateStatus(errorMsg);
919 
920 	    }, false);
921 
922     }
923     
924 };
925 
926 
927 
928 /** Prototype object for each canvas element submitted to Karma in the
929  * Karma() method
930  * @throws {Error} if the name and domId for the canvas element are not specified
931  * @thows {Error} if the supplied domId does not match an element in the DOM
932  * @class This object is the prototype for each canvas element submitted to Karma in the
933  * Karma() method
934  */
935 Karma.kCanvas = {
936     /** Name of the canvas, used internally by karma.js
937      * @type String
938      * @default ''
939      */
940     name : '',
941     /** Width of canvas element
942      * @type Number
943      * @default 0
944      */
945     width: 0,
946     /** Height of canvas element
947      * @type Number
948      * @default 0
949      */
950     height: 0,
951     /**  Whether canvas is visible
952      * @type boolean
953      * @default true
954      */
955     visible: true,
956     /** Element ID for canvas element in html document. This value is read-only
957      * @type String
958      * @default undefined
959      */
960     domId: undefined,
961     /** Reference to the DOM element
962      * @type DOMElement
963      * @default undefined
964      * @example
965      * //You can access all properties and methods of the underlying DOM element
966      * //using the 'node' property
967      * Karma.canvas.someCanvas.node.dispatchEvent( ... some event ...);
968      * var stuff = Karma.canvas.someCanvas.node.innerHTML;
969      * 
970      */
971     node: undefined,
972     /** The 2 Dimensional Rendering context property for this canvas
973      * @type 2DRenderingContext
974      * @default undefined
975      * @example
976      * //Almost all of the context attributes and methods are wrapped in helper functions
977      * //but you can also access them directly using the ctx property
978      * Karma.canvas.someCanvas.ctx.drawImage(someImage, x, y);
979      * Karma.canvas.someCanvas.ctx.fillStyle = "#ffffff";
980      */
981     ctx: undefined,
982 
983     //initializes object with values provides by user
984     _init: function (config) {
985 	for (var option in config){
986 	    if (config.hasOwnProperty(option)){
987 		switch (option){
988 		case "name":
989 		    this.name = config[option];
990 		    break;
991 		case "domId":
992 		    this.domId = config[option];
993 		    break;
994 		case "width":
995 		    if(!this.height){
996 			throw new Error("If you specify a width you must also" +
997 					"specify a height");
998 		    }
999 		    this.width = config[option];
1000 		    break;
1001 		case "height":
1002 		    if(!this.width){
1003 			throw new Error("If you specify a height you must also" +
1004 					"specify a width");
1005 		    }
1006 		    this.height = parseInt(config.option, 10);
1007 		    break;
1008 		case "fps":
1009 		    this.fps = parseInt(config.option, 10);
1010 		    break;
1011 		}
1012 	    }
1013 	}
1014 	
1015 	if(this.domId && document.getElementById(this.domId)){
1016 	       	this.node = document.getElementById(this.domId);
1017 		this.ctx = this.node.getContext('2d');
1018 	} else {
1019 	    throw new Error('you must specify a valid domId that' +
1020 			    'is in your html page');
1021 	}
1022 
1023 	if(!config.height && !config.width){
1024 	    this.width = parseInt(this.node.getAttribute('width'), 10);
1025 	    this.height = parseInt(this.node.getAttribute('height'), 10);
1026 	}
1027 
1028 	return this;
1029     },
1030     /** Clear area of canvas element specified by parameters, if no
1031      * parameters supplied, clears entire canvas
1032      * @param {Number} [x=0] x coordinate, defaults to zero if left blank
1033      * @param {Number} [y=0] y coordinate, defaults to zero if left blank  
1034      * @param {Number} [width=0] width of area to be cleared, defaults 
1035      * entire width of canvas
1036      * @param {Number} [height=0] height of area to be cleared, defaults 
1037      * entire height of canvas
1038      * @returns this
1039      * @example
1040      * 
1041      * k.canvas.ninja.clear();
1042      * // clears the entire ninja canvas
1043      * 
1044      * k.canvas.ninja.clear(0, 10, 20, 30);
1045      * //clears a specific portion of the ninja canvas
1046      * 
1047      */
1048     clear : function ( x, y, width, height ) {
1049 	var that = this;
1050 	that.ctx.clearRect(
1051 	    x || 0,
1052 	    y || 0, 
1053 	    width  || that.width, 
1054 	    height || that.height
1055 	);
1056 	return that;
1057     },
1058   
1059      /** The globalAlpha attribute gives an alpha value that is applied to shapes 
1060      * and images before they are composited onto the canvas
1061      * @param {Number} number in the range from 0.0 to 1.0
1062      * @returns this
1063      */
1064     globalAlpha : function (attribute){
1065 	var name = 'globalAlpha';
1066 	this.ctx[name] = attribute;
1067 	return this;
1068     },
1069  
1070    /** Sets the globalCompositeOperation attribute, which sets how shapes and images 
1071      * are drawn onto the existing bitmap, once they have had globalAlpha and the 
1072      * current transformation matrix applied.
1073      * For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1074      * @param {String} globalCompositeOperation source-atop, 
1075      * source-in, source-out, 
1076      * source-over, destination-atop, destination-in, destination-out, destination-over,
1077      * lighter
1078      * @returns this
1079      */
1080     globalCompositeOperation: function (attribute){
1081 	var name = ' globalCompositeOperation';
1082 	this.ctx[name] = attribute;
1083 	return this;
1084     },
1085 
1086     /** Sets the lineWidth attribute which gives the width of lines, in coordinate space 
1087      * units.
1088      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1089      * @param {Number} lineWidth
1090      * @returns this
1091      */
1092     lineWidth: function (attribute){
1093 	var name = 'lineWidth';
1094 	this.ctx[name] = attribute;
1095 	return this;
1096     },
1097     /** The lineCap attribute defines the type of endings that UAs will place on 
1098      * the end of lines.  
1099      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1100      * @param {String} type butt, round, square 
1101      * @returns this
1102      */
1103     lineCap: function (attribute){
1104 	var name = 'lineCap';
1105 	this.ctx[name] = attribute;
1106 	return this;
1107     },
1108     /** The lineJoin attribute defines the type of corners that UAs will place 
1109      * where two lines meet. The three valid values are bevel, round, and miter. 
1110      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1111      * @param {String} type
1112      * @returns this
1113      */
1114     lineJoin: function (attribute){
1115 	var name = 'lineJoin';
1116 	this.ctx[name] = attribute;
1117 	return this;
1118     },
1119    
1120     /** Sets the miter limit 
1121      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1122      * @param {Number} number
1123      * @returns this
1124      */
1125     miterLimit: function (attribute){
1126 	var name = 'miterLimit';
1127 	this.ctx[name] = attribute;
1128 	return this;
1129     },
1130     /** Sets the font property and takes the same syntax as setting the font property 
1131      *  in CSS
1132      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1133      * @param {String} 
1134      * @returns this
1135      */
1136     font: function (attribute){
1137 	var name = 'font';
1138 	this.ctx[name] = attribute;
1139 	return this;
1140     },
1141 
1142     /** Changes the text alignment. The possible values are start, end, left, right, 
1143      * and center. The default is start. Other values are ignored.
1144      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1145      * @param {string} alignment 
1146      * @returns this
1147      */
1148     textAlign: function (attribute){
1149 	var name = 'textAlign';
1150 	this.ctx[name] = attribute;
1151 	return this;
1152     },
1153 
1154     /** Changes the baseline alignment. If the value is one of top, hanging, middle, 
1155      * alphabetic, ideographic, or bottom, then the value must be changed to the new value.
1156      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1157      * @param {String} alignment
1158      * @returns this
1159      */
1160     textBaseline: function (attribute){
1161 	var name = 'textBaseline';
1162 	this.ctx[name] = attribute;
1163 	return this;
1164     },
1165     
1166     /** Save the current state of the context
1167      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1168      * @param 
1169      * @returns this
1170      */
1171     save : function ( ){
1172 	var name =  'save'; 
1173 	this.ctx[name].apply(this.ctx, arguments);
1174 	return this;
1175     },
1176     /** Restore the saved context
1177      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1178      * @param 
1179      * @returns this
1180      */
1181     restore : function ( ){
1182 	var name =  'restore'; 
1183 	this.ctx[name].apply(this.ctx, arguments);
1184 	return this;
1185     },
1186      /** Perform a scale transformation
1187      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1188      * @param 
1189      * @returns this
1190      */
1191     scale : function ( ){
1192 	var name =  'scale'; 
1193 	this.ctx[name].apply(this.ctx, arguments);
1194 	return this;
1195     },
1196    /** Perform a rotation transformation
1197      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1198      * @param 
1199      * @returns this
1200      */
1201     rotate : function ( ){
1202 	var name =  'rotate'; 
1203 	this.ctx[name].apply(this.ctx, arguments);
1204 	return this;
1205     },
1206      /** Performa a translation transformation
1207      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1208      * @param 
1209      * @returns this
1210      */
1211     translate : function ( ){
1212 	var name =  'translate'; 
1213 	this.ctx[name].apply(this.ctx, arguments);
1214 	return this;
1215     },
1216     
1217     /** Transform the identity matrix
1218      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1219      * @param 
1220      * @returns this
1221      */
1222     transform : function ( ){
1223 	var name =  'transform'; 
1224 	this.ctx[name].apply(this.ctx, arguments);
1225 	return this;
1226     },
1227     /** Set the transform
1228      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1229      * @param 
1230      * @returns this
1231      */
1232     setTransform : function ( ){
1233 	var name =  'setTransform'; 
1234 	this.ctx[name].apply(this.ctx, arguments);
1235 	return this;
1236     },
1237     /** Clear a rectangular area 
1238      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1239      * @param 
1240      * @returns this
1241      */
1242     clearRect : function ( ){
1243 	var name =  'clearRect'; 
1244 	this.ctx[name].apply(this.ctx, arguments);
1245 	return this;
1246     },
1247     /** Fill a rectangular area 
1248      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1249      * @param 
1250      * @returns this
1251      */
1252     fillRect : function ( ){
1253 	var name =  'fillRect'; 
1254 	this.ctx[name].apply(this.ctx, arguments);
1255 	return this;
1256     },
1257       
1258     /** Draw the outline of the rectangle
1259      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1260      * @param 
1261      * @returns this
1262      */
1263     strokeRect : function ( ){
1264 	var name =  'strokeRect'; 
1265 	this.ctx[name].apply(this.ctx, arguments);
1266 	return this;
1267     },
1268     /** Begin a path 
1269      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1270      * @param 
1271      * @returns this
1272      */
1273     beginPath : function ( ){
1274 	var name =  'beginPath'; 
1275 	this.ctx[name].apply(this.ctx, arguments);
1276 	return this;
1277     },
1278     /** End a path 
1279      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1280      * @param 
1281      * @returns this
1282      */
1283     closePath : function ( ){
1284 	var name =  'closePath'; 
1285 	this.ctx[name].apply(this.ctx, arguments);
1286 	return this;
1287     },
1288     /** Move to specified coordinates 
1289      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1290      * @param 
1291      * @returns this
1292      */
1293     moveTo : function ( ){
1294 	var name =  'moveTo'; 
1295 	this.ctx[name].apply(this.ctx, arguments);
1296 	return this;
1297     },
1298 
1299 
1300     /** Draw a line to the given coordinates 
1301      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1302      * @param 
1303      * @returns this
1304      */
1305     lineTo : function ( ){
1306 	var name =  'lineTo'; 
1307 	this.ctx[name].apply(this.ctx, arguments);
1308 	return this;
1309     },
1310 
1311     /** Draw a quadratic curve to given coordinates 
1312      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1313      * @param 
1314      * @returns this
1315      */
1316     quadraticCurveTo : function ( ){
1317 	var name =  'quadraticCurveTo'; 
1318 	this.ctx[name].apply(this.ctx, arguments);
1319 	return this;
1320     },
1321     /** Draw a bezier curve to given coordinates 
1322      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1323      * @param 
1324      * @returns this
1325      */
1326     bezierCurveTo : function ( ){
1327 	var name =  'bezierCurveTo'; 
1328 	this.ctx[name].apply(this.ctx, arguments);
1329 	return this;
1330     },
1331     /** Draw an arc to the given points
1332      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1333      * @param 
1334      * @returns this
1335      */
1336     arcTo : function ( ){
1337 	var name =  'arcTo'; 
1338 	this.ctx[name].apply(this.ctx, arguments);
1339 	return this;
1340     },
1341     /** Create an arc 
1342      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1343      * @param 
1344      * @returns this
1345      */
1346     arc : function ( ){
1347 	var name =  'arc'; 
1348 	this.ctx[name].apply(this.ctx, arguments);
1349 	return this;
1350     },
1351 
1352     /** Create a rectangle 
1353      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1354      * @param 
1355      * @returns this
1356      */
1357     rect : function ( ){
1358 	var name =  'rect'; 
1359 	this.ctx[name].apply(this.ctx, arguments);
1360 	return this;
1361     },
1362     /** fill in the current subpaths with the current fillstyle
1363      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1364      * @param 
1365      * @returns this
1366      */
1367     fill : function ( ){
1368 	var name =  'fill'; 
1369 	this.ctx[name].apply(this.ctx, arguments);
1370 	return this;
1371     },
1372     /** Stroke the subpaths
1373      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1374      * @param 
1375      * @returns this
1376      */
1377     stroke : function ( ){
1378 	var name =  'stroke'; 
1379 	this.ctx[name].apply(this.ctx, arguments);
1380 	return this;
1381     },
1382     
1383     /** description 
1384      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1385      * @param 
1386      * @returns this
1387      */
1388     clip : function ( ){
1389 	var name =  'clip'; 
1390 	this.ctx[name].apply(this.ctx, arguments);
1391 	return this;
1392     },
1393     /** description 
1394      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1395      * @param 
1396      * @returns this
1397      */
1398     fillText : function ( ){
1399 	var name =  'fillText'; 
1400 	this.ctx[name].apply(this.ctx, arguments);
1401 	return this;
1402     },
1403     /** description 
1404      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1405      * @param 
1406      * @returns this
1407      */
1408     strokeText : function ( ){
1409 	var name =  'strokeText'; 
1410 	this.ctx[name].apply(this.ctx, arguments);
1411 	return this;
1412     },
1413     /** description 
1414      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1415      * @param 
1416      * @returns this
1417      */
1418     measureText : function ( ){
1419 	var name =  'measureText'; 
1420 	this.ctx[name].apply(this.ctx, arguments);
1421 	return this;
1422     },
1423     /** description 
1424      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1425      * @param 
1426      * @returns this
1427      */
1428     isPointInPath : function ( ){
1429 	var name =  'isPointInPath'; 
1430 	this.ctx[name].apply(this.ctx, arguments);
1431 	return this;
1432     },
1433     
1434     /** Sets the stroke style 
1435      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1436      * @param 
1437      * @returns this
1438      */
1439     strokeStyle: function (attribute){
1440 	var name = 'strokeStyle';
1441 	this.ctx[name] = attribute;
1442 	return this;
1443     },
1444 
1445     /** Sets the fill style
1446      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1447      * @param 
1448      * @returns this
1449      */
1450     fillStyle: function (attribute){
1451 	var name = 'fillStyle';
1452 	this.ctx[name] = attribute;
1453 	return this;
1454     },
1455      /** description 
1456      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1457      * @param 
1458      * @returns this
1459      */
1460     createLinearGradient : function ( ){
1461 	var name =  'createLinearGradient'; 
1462 	this.ctx[name].apply(this.ctx, arguments);
1463 	return this;
1464     },
1465     /** description 
1466      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1467      * @param 
1468      * @returns this
1469      */
1470     createRadialGradient : function ( ){
1471 	var name =  'createRadialGradient'; 
1472 	this.ctx[name].apply(this.ctx, arguments);
1473 	return this;
1474     },
1475     /** description 
1476      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1477      * @param 
1478      * @returns this
1479      */
1480     createPattern : function ( ){
1481 	var name =  'createPattern'; 
1482 	this.ctx[name].apply(this.ctx, arguments);
1483 	return this;
1484     },
1485      /** description 
1486      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1487      * @param 
1488      * @returns this
1489      */
1490     shadowOffsetX: function (attribute){
1491 	var name = 'shadowOffsetX';
1492 	this.ctx[name] = attribute;
1493 	return this;
1494     },
1495     /** description 
1496      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1497      * @param 
1498      * @returns this
1499      */
1500     shadowOffsetY: function (attribute){
1501 	var name = 'shadowOffsetY';
1502 	this.ctx[name] = attribute;
1503 	return this;
1504     },
1505     /** description 
1506      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1507      * @param 
1508      * @returns this
1509      */
1510     shadowBlur: function (attribute){
1511 	var name = 'shadowBlur';
1512 	this.ctx[name] = attribute;
1513 	return this;
1514     },
1515     /** description 
1516      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1517      * @param 
1518      * @returns this
1519      */
1520     shadowColor: function (attribute){
1521 	var name = 'shadowColor';
1522 	this.ctx[name] = attribute;
1523 	return this;
1524     },
1525    /** description 
1526      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1527      * @param 
1528      * @returns this
1529      */
1530     drawImage : function ( ){
1531 	var name =  'drawImage'; 
1532 	this.ctx[name].apply(this.ctx, arguments);
1533 	return this;
1534     },
1535     /** description 
1536      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1537      * @param 
1538      * @returns this
1539      */
1540     getImageData : function ( ){
1541 	var name =  'getImageData'; 
1542 	this.ctx[name].apply(this.ctx, arguments);
1543 	return this;
1544     },
1545     /** description 
1546      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1547      * @param 
1548      * @returns this
1549      */
1550     putImageData : function ( ){
1551 	var name =  'putImageData'; 
1552 	this.ctx[name].apply(this.ctx, arguments);
1553 	return this;
1554     },
1555     /** description 
1556      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1557      * @param 
1558      * @returns this
1559      */
1560     createImageData : function ( ){
1561 	var name =  'createImageData'; 
1562 	this.ctx[name].apply(this.ctx, arguments);
1563 	return this;
1564     },
1565     /** description 
1566      *  For full details see <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalcompositeoperation">W3C docs</a>
1567      * @param 
1568      * @returns this
1569      */
1570     drawWindow : function ( ){
1571 	var name =  'drawWindow'; 
1572 	this.ctx[name].apply(this.ctx, arguments);
1573 	return this;
1574     },
1575     
1576 
1577  
1578    
1579 };
1580 
1581 
1582 /** Prototype object for each svg element submitted to Karma in the
1583  * Karma() method
1584  * @throws {Error} if the name and domId for the svg element are not specified
1585  * @thows {Error} if the supplied domId does not match an element in the DOM
1586  * @class This object is the prototype for each svg element submitted to Karma in the
1587  * Karma() method
1588  */
1589 Karma.kSvg = {
1590     /** name of instance, used internally 
1591      * @typeof string
1592      * @default ""
1593      */
1594     name : "",
1595     /** width of element 
1596      * @type number
1597      * @default 0
1598      */
1599     width: 0,
1600     /** height of element 
1601      * @type number
1602      * @default 0
1603      */
1604     height: 0,
1605     /** Status of element, either "loaded" or "error"
1606      * @type string
1607      * @default ""
1608      */
1609     status: "",
1610     /**  Whether canvas is visible. This value is read-only
1611      * @type boolean
1612      * @default true
1613      */
1614     visible: true,
1615     /** Element ID for canvas element in html document. 
1616      * @type String
1617      * @default undefined
1618      */
1619     domId: undefined,
1620     /** Reference to the DOM element.
1621      * @type DOMElement
1622      * @default undefined
1623      * @example 
1624      * //You can access all properties and methods of the underlying DOM element
1625      * //using the 'node' property
1626      * Karma.svg.someSvg.node.dispatchEvent;
1627      * Karma.svg.someSvg.node.addEvenListener(...);
1628      */
1629     node: undefined,
1630     /** Reference to the SVGDocument. You can use the this.doc to manipulate 
1631      * the SVG document 
1632      * @type SVGDocument
1633      * @default undefined
1634      * @example
1635      * var myElem = Karma.svg.someSvg.doc.getElementById('foobar');
1636      * Karma.svg.someSvg.doc.createElement(...);
1637      * Karma.svg.someSvg.doc.removeChild(someNode);
1638      *     
1639      */
1640     doc: undefined,
1641     /** Reference to the root element of the SVG Document 
1642      * @type DocumentElement
1643      * @default undefined
1644      * @example
1645      * // The root element is equivalent to "document" in a regular html document
1646      * // The root attribute is used frequently with the jQuery SVG plugin for CSS selectors
1647      * $('#someId', Karma.svg.someSvg.root).css(.. manipulate css attributes ...);
1648      */
1649     root: undefined,
1650     _localized : undefined,
1651     _init: function (config) {
1652 	Karma._counters.total++;
1653 
1654 	for (var option in config){
1655 	    if (config.hasOwnProperty(option)){
1656 		switch (option){
1657 		case "name":
1658 		    this.name = config[option];
1659 		    break;
1660 		case "domId":
1661 		    this.domId = config[option];
1662 		    break;
1663 		case "width":
1664 		    if(!this.height){
1665 			throw new Error("If you specify a width you must also" +
1666 					"specify a height");
1667 		    }
1668 		    this.width = parseInt(config[option], 10);
1669 		    break;
1670 		case "height":
1671 		    if(!this.width){
1672 			throw new Error("If you specify a height you must also" +
1673 					"specify a width");
1674 		    }
1675 		    this.height = config[option];
1676 		    break;
1677 		}
1678 	    }
1679 	}
1680 	
1681 	if(this.domId && document.getElementById(this.domId)){
1682 	       	this.node = document.getElementById(this.domId);
1683 	} else {
1684 	    throw new Error('you must specify a valid domId that' +
1685 			    'is in your html page');
1686 	}
1687 
1688 	if(!config.height && !config.width){
1689 	    this.width = parseInt(this.node.getAttribute('width'), 10);
1690 	    this.height = parseInt(this.node.getAttribute('height'), 10);
1691 	}
1692 	
1693 	var that = this;
1694 	that._addEventHandlers();
1695 		   
1696 	return this;
1697 	
1698 	
1699     },
1700     _addEventHandlers : function () {
1701 	var that = this;	
1702 	that.doc = that.node.getSVGDocument();	
1703 	that.node.addEventListener(
1704 		"load", 
1705 	    function (e) { 
1706 		that.doc = that.node.getSVGDocument();    
1707 		that.root = that.doc.documentElement;
1708 		Karma._counters.loaded++;
1709 		Karma._updateStatus();
1710 		that.status = "loaded";
1711 	    }, false);
1712 
1713 	that.node.addEventListener(
1714 	    "error", 
1715 	    function (e) { 
1716 		Karma._counters.loaded--;
1717 		Karma._counters.errors++;
1718 		that.status = "error";
1719 		var errorMsg = "Error: " + that._type.toUpperCase() +
1720 		    " " + that.name + " cannot be loaded."; 
1721 		Karma._updateStatus(errorMsg);
1722 	    }, 
1723 	    false);
1724 	that.node.addEventListener(
1725 	    "abort", 
1726 	    function (e) { 
1727 		that.status = "aborted";
1728 		var errorMsg = "ABORT: " + that._type.toUpperCase() +
1729 		    " " + that.name + " loading was aborted."; 
1730 		Karma._updateStatus(errorMsg);
1731 
1732 	    }, false);
1733 
1734     }
1735 };
1736