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 = "" + 900 + "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 Karma.scaleWindow = function(){ 279 var width = "1200px"; 280 var height = "900px"; 281 var viewportHeight = "760px"; 282 var $body = $('body'); 283 var $kMain = $('#kMain'); 284 285 if (window.innerWidth < 1150){ 286 width = "950px"; 287 height = "600px"; 288 viewportHeight = "460px"; 289 $body.css('border', '2px solid black'); 290 291 // 460/760 * 16 = 9.6 292 $kMain.css('font-size', '9.6px'); 293 } 294 295 $body.css({border: '2px solid black', width: width, height: height}); 296 $kMain.css({width: width, height: viewportHeight}); 297 298 299 }; 300 301 // Below are geometry and math helper methods 302 303 /** 304 * Converts a value from degrees to radians. 305 * @param {Number} angle The angle in degrees 306 * @returns {Number} The angle in radians 307 */ 308 Karma.radians = function( angle ){ 309 return ( angle / 180 ) * Math.PI; 310 }; 311 312 /** 313 * Gets the square of the Euclidian (ordinary) distance between 2 points. 314 * @param {Object} Point No. 0 315 * @param {Number} Point0.x 316 * @param {Number} Point0.y 317 * @param {Object} Point No. 1 318 * @param {Number} Point1.x 319 * @param {Number} Point1.y 320 * @returns {Number} The square of the Euclidian distance 321 * @example 322 * 323 * p0 = {x:0, y:1}; 324 * p1 = {x:50, y:70}; 325 * var d = distance2(p0, p1); 326 * 327 */ 328 Karma.distance2 = function ( p0, p1 ) { 329 return (p1.x - p0.x) * (p1.x - p0.x) + (p1.y - p1.y) * (p1.y - p1.y); 330 }; 331 332 /** 333 * Gets the Euclidian (ordinary) distance between 2 points.<br> 334 * <b>Warning:</b> It's slower than distance2 function 335 * @param {Object} Point No. 0 336 * @param {Number} Point0.x 337 * @param {Number} Point0.y 338 * @param {Object} Point No. 1 339 * @param {Number} Point1.x 340 * @param {Number} Point1.y 341 * @returns {Number} The Euclidian distance 342 * @example 343 * 344 * p0 = {x:0, y:1}; 345 * p1 = {x:50, y:70}; 346 * var d = distance2(p0, p1); 347 * 348 */ 349 Karma.distance = function ( p0, p1 ) { 350 return Math.sqrt( this.distance2( p0, p1 ) ); 351 }; 352 353 /** Returns a random number within the range provided 354 * @param {Number} lower limit of the range, lowest number that can be returned 355 * @param {Number} upper limit of the range, highest number that can be returned 356 * @returns {Number} number that is >= lower and <= upper 357 * @example 358 * 359 * var num = rand(0, 10); 360 * 361 * //num could be 0, 1, 2, 3 ... or 10 362 * 363 */ 364 Karma.rand = function ( lower, upper ){ 365 return Math.floor(Math.random() * (upper - lower + 1) + lower); 366 }; 367 368 369 Karma.extend(Karma, { 370 /** This is the global locale as passed to Karma(), 371 * such as "en", "es_SP" 372 * @fieldOf Karma 373 * @property {string} locale This is the global locale as passed to Karma() 374 * @default 'en' 375 */ 376 locale : 'en', 377 /** Collection of images with special helper 378 * methods added to each reference 379 * @fieldOf Karma 380 * @type object 381 * @default empty object 382 */ 383 image : {}, 384 /** Collection of audio files with special helper 385 * methods added to each reference 386 * @fieldOf Karma 387 * @type object 388 * @default empty object 389 */ 390 audio : {}, 391 /** Collection of html 5 canvases with special helper 392 * methods added to each reference 393 * @fieldOf Karma 394 * @type object 395 * @default empty object 396 */ 397 canvas : {}, 398 /** Collection of svgs with special helper 399 * methods added to each reference 400 * @fieldOf Karma 401 * @type object 402 * @default empty object 403 */ 404 svg : {}, 405 /** Collection of videos with special helper 406 * methods added to each reference 407 * @fieldOf Karma 408 * @type object 409 * @default empty object 410 */ 411 video : {}, 412 _localized : false, 413 _assetPath : "assets/", 414 _localePath : "", 415 _initialized : false, 416 _statusDiv: undefined, 417 _loaderDiv : undefined, 418 _counters : { total : 0, errors : 0, loaded : 0}, 419 420 //This constructs the Karma object per values provided by the user 421 _init: function(options) { 422 this._initialized = true; 423 424 //set up message that show count of assets loaded 425 //and has an ordered list to append error messages to 426 var _statusDiv = this._statusDiv = document.createElement('div'); 427 this._loaderDiv = this._loaderDiv = document.createElement('div'); 428 var errorList = document.createElement('ol'); 429 430 _statusDiv.setAttribute('id', 'karma-status'); 431 _statusDiv.setAttribute('style', 'position:absolute;'); 432 _statusDiv.innerHTML = 'Karma is loading ...'; 433 this._loaderDiv.setAttribute('id', 'karma-loader'); 434 this._loaderDiv.setAttribute('class', 'status'); 435 errorList.setAttribute('id', 'errorList'); 436 437 _statusDiv.appendChild(this._loaderDiv); 438 this._statusDiv.appendChild(errorList); 439 document.body.appendChild(_statusDiv); 440 441 //regular expression that matches the name of aprivate property 442 // the karma object 443 var regexPrivate = new RegExp('^_.*'); 444 445 for ( var option in options ) { 446 if (options.hasOwnProperty(option)){ 447 if (option === "image" || option === "audio" || option === 448 "svg" || option === "video" || option === "canvas"){ 449 450 if(!(options[option] instanceof Array)){ 451 throw new Error("" + option + " must be an array"); 452 } else if (options[option].length === 0){ 453 continue; 454 } 455 } else if (regexPrivate.test(option)){ 456 //don't overwrite a private property of karma object 457 continue; 458 } 459 460 switch (option){ 461 case "locale": 462 463 if (this._isValidLocale(options[option])){ 464 this.locale = this._normalizeLocale(options[option]); 465 this._localized = true; 466 this._localePath = Karma._computeLocalePath(this.locale); 467 } else { 468 throw new Error("locale provided to karma._init() is invalid"); 469 } 470 471 break; 472 case "image": 473 options[option]._type = 'image'; 474 Karma._makeCollection(options[option], 'image'); 475 break; 476 case "audio": 477 options[option]._type = 'audio'; 478 Karma._makeCollection(options[option], 'audio'); 479 break; 480 case "video": 481 options[option]._type = 'video'; 482 Karma._makeCollection(options[option], 'video'); 483 break; 484 case "svg": 485 options[option]._type = 'svg'; 486 Karma._makeCollection(options[option], 'svg'); 487 break; 488 case "canvas": 489 options[option]._type = 'canvas'; 490 Karma._makeCollection(options[option], 'canvas'); 491 break; 492 } 493 } 494 } 495 496 497 498 return this; 499 }, 500 501 /** Waits until all assets loaded(ready), then calls callback cb 502 * @memberOf Karma 503 * @param {Function} [cb] callback function 504 * @returns this 505 * @throws {Error} if Karma is not initialized with the 506 * Karma({ options }) function 507 * @example 508 * 509 * var k = Karma({ . . . your assets here . . . }); 510 * k.ready(function(){ .. your code here . . .}); 511 * 512 * your code will not be called until all assets have been loaded 513 * into collections 514 * 515 */ 516 ready : function( cb ) { 517 var that = this; 518 if (Karma._initialized !== true){ 519 throw new Error("Karma not initialized"); 520 } 521 522 if (this._counters.loaded !== this._counters.total){ 523 setTimeout(function(){ that.ready(cb);}, 5); 524 } else if (cb) { 525 //hide the "Karma is loading..." message 526 this._statusDiv.setAttribute('style', 'display:none;'); 527 528 cb(); 529 } else if (!cb) { 530 //hide the "Karma is loading..." message 531 this._statusDiv.setAttribute('style', 'display:none;'); 532 533 //if no options passed, show it works message 534 this._showStarterMessage(); 535 } 536 537 538 539 540 return this; 541 }, 542 543 //Display Apache-like "It works" message if no options 544 _showStarterMessage : function (){ 545 var starterMsg = document.createElement('div'); 546 starterMsg.setAttribute('id', 'starterMsg'); 547 starterMsg.innerHTML = "<h1>It Works</h1>"; 548 document.body.appendChild(starterMsg); 549 }, 550 551 //Updates visible counter of how many assets are loaded 552 _updateStatus : function (errorMsg) { 553 var loaded = this._counters.loaded; 554 var total = this._counters.total; 555 var errors = this._counters.errors; 556 this._loaderDiv.innerHTML = "Loaded " + loaded + " / " + total + 557 "" + (errors > 0 ? " Errors [ " + errors +" ]" : ''); 558 if (errorMsg) { 559 var liError = document.createElement('li'); 560 liError.innerHTML = errorMsg; 561 var errorList = document.getElementById('errorList'); 562 errorList.appendChild(liError); 563 } 564 }, 565 566 //matches 2 letter country code then optionally 567 //a dash or underscore followed by a country or language identifier 568 //i currently only allow a language identifier 2-3 chars long 569 _isValidLocale : function (locale) { 570 var localeRegex = new RegExp('^[a-zA-Z][a-zA-Z]([-_][a-zA-z]{2,3})?$'); 571 return localeRegex.test(locale); 572 }, 573 574 _normalizeLocale : function(locale) { 575 var lang = ""; 576 var country = ""; 577 var divider = ""; 578 579 lang = locale.slice(0, 2).toLowerCase(); 580 divider = "_"; 581 country = locale.slice(3, 6).toUpperCase(); 582 583 return locale.length > 2 ? "" + lang + divider + country : lang; 584 }, 585 586 587 588 }); 589 590 //Helper functions for creating assets 591 Karma._isLocalized = function (boolLocalized) { 592 if (typeof boolLocalized === "boolean" ) { 593 if(boolLocalized === true && 594 Karma.locale === undefined){ 595 throw new Error("You cannot localize a media asset" + 596 " if the global locale for Karma isn't set"); 597 } else { 598 return boolLocalized; 599 } 600 } else if (typeof boolLocalized === undefined){ 601 return false; 602 } else{ 603 throw new Error("This is not a valid value for the localized option"); 604 } 605 }; 606 607 Karma._computeLocalePath = function(locale) { 608 return Karma._assetPath + locale + "/"; 609 }; 610 611 612 613 614 Karma._makeCollection = function (configs, type){ 615 var makeAsset = function (config){ 616 var asset = undefined; 617 var target = undefined; 618 switch(type){ 619 case "image": 620 target = Karma.kImage; 621 break; 622 case "audio": 623 target = Karma.kAudio; 624 break; 625 case "video": 626 target = Karma.kVideo; 627 break; 628 case "svg": 629 target = Karma.kSvg; 630 break; 631 case "canvas": 632 target = Karma.kCanvas; 633 break; 634 } 635 636 asset = Karma.create(target)._init(config); 637 Karma[type][config.name] = asset; 638 }; 639 640 configs.forEach(function(config){ makeAsset(config);}); 641 }; 642 643 644 645 646 647 //Prototype objects for assets 648 649 650 /** Prototype object for images 651 * @class This object is the prototype for images submitted to Karma in the 652 * Karma() method 653 * @ throws {Error} if the image asset is set to be localized but 654 * the global locale is not set on the Karma object 655 * @ throws {Error} if the name and file properties are not supplied 656 * @example 657 * kImage is the prototype object for images. This 'media' asset is loaded 658 * in a distinctly different way from the canvas or svg assets. 659 * 660 */ 661 Karma.kImage = 662 { 663 /** file location of image 664 * @type String 665 * @default "" 666 */ 667 file : "", 668 /** media object 669 * @type Image 670 * @default undefined 671 */ 672 media : undefined, 673 //actual path to the file 674 _path : "", 675 //if using localized version of this image 676 _localized : false, 677 _type : "image", 678 //initializes kImage instance with values provided by user 679 _init : function (image) { 680 image._localized = image._localized || false; 681 Karma._counters.total++; 682 683 if (image.name === undefined || image.file === undefined){ 684 throw new Error("properties name and file have to be defined"); 685 } else { 686 this.name = image.name; 687 this.file = image.file; 688 } 689 690 this.media = new Image(); 691 692 if(Karma._isLocalized(image._localized)){ 693 this._localized = image._localized; 694 this._path = Karma._localePath + "image/"; 695 } else { 696 this._path = Karma._assetPath + "image/"; 697 } 698 699 //IMPORTANT: This one magic line loads the file 700 this.media.src = this.src = this._path + this.file; 701 702 //add event handlers 703 this._addEventHandlers(); 704 705 706 return this; 707 }, 708 //Adds event handlers to update the counters when 709 //the image is successfully or unsuccessfully loaded 710 _addEventHandlers : function () { 711 var that = this; 712 713 that.media.addEventListener( 714 "load", 715 function (e) { 716 Karma._counters.loaded++; 717 Karma._updateStatus(); 718 that.status = "loaded";}, false); 719 720 that.media.addEventListener( 721 "error", 722 function (e) { 723 Karma._counters.errors++; 724 that.status = "error"; 725 var errorMsg = "Error: " + that._type.toUpperCase() + 726 " " + that.name + " cannot be loaded."; 727 Karma._updateStatus(errorMsg); 728 }, 729 false); 730 that.media.addEventListener( 731 "abort", 732 function (e) { 733 Karma._counters.total++; 734 that.status = "aborted"; 735 var errorMsg = "ABORT: " + that._type.toUpperCase() + 736 " " + that.name + " loading was aborted."; 737 Karma._updateStatus(errorMsg); 738 739 }, false); 740 } 741 742 }; 743 744 /** Prototype object for audio files 745 * @class This object is the prototype for audio files submitted to Karma in the 746 * Karma() method 747 * @ throws {Error} if the individual audio asset is set to be localized but 748 * the globale locale is not set on the Karma object 749 * @ throws {Error} if the name and file properties are not supplied 750 * @example 751 * kAudio is the prototype object for audio 752 * The audio assets are loaded in a distinctly different way 753 * from the canvas or svg assets. They also have distinctly different 754 * helper methods 755 * 756 * You initialize the kAudio assets by passing an array of objects 757 */ 758 Karma.kAudio = { 759 /** file location of asset 760 * @type String 761 * @default "" 762 */ 763 file : "", 764 /** Media object. You can access the src, autobuffer, autoplay, loop, and 765 * controls attributes 766 * 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> 767 * @type Audio 768 * @default undefined 769 */ 770 media : undefined, 771 //actual path to the file 772 _path : "", 773 //if using localized version of this asset 774 _localized : false, 775 _type : "audio", 776 //initializes kAudio instance with values provided by user 777 _init : function (audio) { 778 audio._localized = audio._localized || false; 779 Karma._counters.total++; 780 781 if (audio.name === undefined || audio.file === undefined){ 782 throw new Error("properties name and file have to be defined"); 783 } else { 784 this.name = audio.name; 785 this.file = audio.file; 786 } 787 788 this.media = new Audio(); 789 790 if(Karma._isLocalized(audio._localized)){ 791 this._localized = audio._localized; 792 this._path = Karma._localePath + "audio/"; 793 } else { 794 this._path = Karma._assetPath + "audio/"; 795 } 796 797 798 //IMPORTANT: This one magic line loads the file 799 this.media.src = this.src = this._path + this.file; 800 801 //add event handlers 802 this._addEventHandlers(); 803 804 this.media.autobuffer = true; 805 this.media.load(); 806 807 808 return this; 809 }, 810 //Adds event handlers to update the counters when 811 //the asset is successfully or unsuccessfully loaded 812 _addEventHandlers : function () { 813 var that = this; 814 //'canplaythrough' event is a Browser Hack recommended by chromium devs 815 //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 816 817 that.media.addEventListener( 818 "canplaythrough", 819 function (e) { 820 Karma._counters.loaded++; 821 Karma._updateStatus(); 822 that.status = "loaded";}, false); 823 824 that.media.addEventListener( 825 "error", 826 function (e) { 827 Karma._counters.errors++; 828 that.status = "error"; 829 var errorMsg = "Error: " + that._type.toUpperCase() + 830 " " + that.name + " cannot be loaded."; 831 Karma._updateStatus(errorMsg); 832 }, 833 false); 834 that.media.addEventListener( 835 "abort", 836 function (e) { 837 Karma._counters.total++; 838 that.status = "aborted"; 839 var errorMsg = "ABORT: " + that._type.toUpperCase() + 840 " " + that.name + " loading was aborted."; 841 Karma._updateStatus(errorMsg); 842 843 }, false); 844 845 }, 846 /** Plays the audio file */ 847 play : function () { 848 this.media.play(); 849 } 850 851 }; 852 853 /** NYI:Prototype object for Video files 854 * @class Not Yet Implemented:This object is the prototype for video files submitted 855 * to Karma in the Karma() method 856 * @ throws {Error} if the individual video asset is set to be localized but 857 * the globale locale is not set on the Karma object 858 * @ throws {Error} if the name and file properties are not supplied 859 */ 860 Karma.kVideo = { 861 /** file location of asset 862 * @type String 863 * @default "" 864 */ 865 file : "", 866 /** media object 867 * @type Video 868 * @default undefined 869 */ 870 media : undefined, 871 //actual path to the file 872 _path : "", 873 //if using localized version of this asset 874 _localized : false, 875 _type : "video", 876 //initializes kVideo instance with values provided by user 877 _init : function (video) { 878 //Not Yet Implemented 879 Karma._counters.errors++; 880 throw new Error("Video is not Yet Implemented"); 881 882 video._localized = video._localized || false; 883 Karma._counters.total++; 884 885 if (video.name === undefined || video.file === undefined){ 886 throw new Error("properties name and file have to be defined"); 887 } else { 888 this.name = video.name; 889 this.file = video.file; 890 } 891 892 this.media = new Video(); 893 894 if(Karma._isLocalized(video._localized)){ 895 this._localized = video._localized; 896 this._path = Karma._localePath + "video/"; 897 } else { 898 this._path = Karma._assetPath + "video/"; 899 } 900 901 902 //IMPORTANT: This one magic line loads the file 903 this.media.src = this.src = this._path + this.file; 904 905 //add event handlers 906 this._addEventHandlers(); 907 908 return this; 909 }, 910 //Adds event handlers to update the counters when 911 //the asset is successfully or unsuccessfully loaded 912 _addEventHandlers : function () { 913 var that = this; 914 //'canplaythrough' event is a Browser Hack recommended by chromium devs 915 //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 916 917 that.media.addEventListener( 918 "canplaythrough", 919 function (e) { 920 Karma._counters.loaded++; 921 Karma._updateStatus(); 922 that.status = "loaded";}, false); 923 924 that.media.addEventListener( 925 "error", 926 function (e) { 927 Karma._counters.errors++; 928 that.status = "error"; 929 var errorMsg = "Error: " + that._type.toUpperCase() + 930 " " + that.name + " cannot be loaded."; 931 Karma._updateStatus(errorMsg); 932 }, 933 false); 934 that.media.addEventListener( 935 "abort", 936 function (e) { 937 Karma._counters.total++; 938 that.status = "aborted"; 939 var errorMsg = "ABORT: " + that._type.toUpperCase() + 940 " " + that.name + " loading was aborted."; 941 Karma._updateStatus(errorMsg); 942 943 }, false); 944 945 } 946 947 }; 948 949 950 951 /** Prototype object for each canvas element submitted to Karma in the 952 * Karma() method 953 * @throws {Error} if the name and domId for the canvas element are not specified 954 * @thows {Error} if the supplied domId does not match an element in the DOM 955 * @class This object is the prototype for each canvas element submitted to Karma in the 956 * Karma() method 957 */ 958 Karma.kCanvas = { 959 /** Name of the canvas, used internally by karma.js 960 * @type String 961 * @default '' 962 */ 963 name : '', 964 /** Width of canvas element 965 * @type Number 966 * @default 0 967 */ 968 width: 0, 969 /** Height of canvas element 970 * @type Number 971 * @default 0 972 */ 973 height: 0, 974 /** Whether canvas is visible 975 * @type boolean 976 * @default true 977 */ 978 visible: true, 979 /** Element ID for canvas element in html document. This value is read-only 980 * @type String 981 * @default undefined 982 */ 983 domId: undefined, 984 /** Reference to the DOM element 985 * @type DOMElement 986 * @default undefined 987 * @example 988 * //You can access all properties and methods of the underlying DOM element 989 * //using the 'node' property 990 * Karma.canvas.someCanvas.node.dispatchEvent( ... some event ...); 991 * var stuff = Karma.canvas.someCanvas.node.innerHTML; 992 * 993 */ 994 node: undefined, 995 /** The 2 Dimensional Rendering context property for this canvas 996 * @type 2DRenderingContext 997 * @default undefined 998 * @example 999 * //Almost all of the context attributes and methods are wrapped in helper functions 1000 * //but you can also access them directly using the ctx property 1001 * Karma.canvas.someCanvas.ctx.drawImage(someImage, x, y); 1002 * Karma.canvas.someCanvas.ctx.fillStyle = "#ffffff"; 1003 */ 1004 ctx: undefined, 1005 1006 //initializes object with values provides by user 1007 _init: function (config) { 1008 for (var option in config){ 1009 if (config.hasOwnProperty(option)){ 1010 switch (option){ 1011 case "name": 1012 this.name = config[option]; 1013 break; 1014 case "domId": 1015 this.domId = config[option]; 1016 break; 1017 case "width": 1018 if(!this.height){ 1019 throw new Error("If you specify a width you must also" + 1020 "specify a height"); 1021 } 1022 this.width = config[option]; 1023 break; 1024 case "height": 1025 if(!this.width){ 1026 throw new Error("If you specify a height you must also" + 1027 "specify a width"); 1028 } 1029 this.height = parseInt(config.option, 10); 1030 break; 1031 case "fps": 1032 this.fps = parseInt(config.option, 10); 1033 break; 1034 } 1035 } 1036 } 1037 1038 if(this.domId && document.getElementById(this.domId)){ 1039 this.node = document.getElementById(this.domId); 1040 this.ctx = this.node.getContext('2d'); 1041 } else { 1042 throw new Error('you must specify a valid domId that' + 1043 'is in your html page'); 1044 } 1045 1046 if(!config.height && !config.width){ 1047 this.width = parseInt(this.node.getAttribute('width'), 10); 1048 this.height = parseInt(this.node.getAttribute('height'), 10); 1049 } 1050 1051 return this; 1052 }, 1053 /** Clear area of canvas element specified by parameters, if no 1054 * parameters supplied, clears entire canvas 1055 * @param {Number} [x=0] x coordinate, defaults to zero if left blank 1056 * @param {Number} [y=0] y coordinate, defaults to zero if left blank 1057 * @param {Number} [width=0] width of area to be cleared, defaults 1058 * entire width of canvas 1059 * @param {Number} [height=0] height of area to be cleared, defaults 1060 * entire height of canvas 1061 * @returns this 1062 * @example 1063 * 1064 * k.canvas.ninja.clear(); 1065 * // clears the entire ninja canvas 1066 * 1067 * k.canvas.ninja.clear(0, 10, 20, 30); 1068 * //clears a specific portion of the ninja canvas 1069 * 1070 */ 1071 clear : function ( x, y, width, height ) { 1072 var that = this; 1073 that.ctx.clearRect( 1074 x || 0, 1075 y || 0, 1076 width || that.width, 1077 height || that.height 1078 ); 1079 return that; 1080 }, 1081 1082 /** The globalAlpha attribute gives an alpha value that is applied to shapes 1083 * and images before they are composited onto the canvas 1084 * @param {Number} number in the range from 0.0 to 1.0 1085 * @returns this 1086 */ 1087 globalAlpha : function (attribute){ 1088 var name = 'globalAlpha'; 1089 this.ctx[name] = attribute; 1090 return this; 1091 }, 1092 1093 /** Sets the globalCompositeOperation attribute, which sets how shapes and images 1094 * are drawn onto the existing bitmap, once they have had globalAlpha and the 1095 * current transformation matrix applied. 1096 * 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> 1097 * @param {String} globalCompositeOperation source-atop, 1098 * source-in, source-out, 1099 * source-over, destination-atop, destination-in, destination-out, destination-over, 1100 * lighter 1101 * @returns this 1102 */ 1103 globalCompositeOperation: function (attribute){ 1104 var name = ' globalCompositeOperation'; 1105 this.ctx[name] = attribute; 1106 return this; 1107 }, 1108 1109 /** Sets the lineWidth attribute which gives the width of lines, in coordinate space 1110 * units. 1111 * 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> 1112 * @param {Number} lineWidth 1113 * @returns this 1114 */ 1115 lineWidth: function (attribute){ 1116 var name = 'lineWidth'; 1117 this.ctx[name] = attribute; 1118 return this; 1119 }, 1120 /** The lineCap attribute defines the type of endings that UAs will place on 1121 * the end of lines. 1122 * 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> 1123 * @param {String} type butt, round, square 1124 * @returns this 1125 */ 1126 lineCap: function (attribute){ 1127 var name = 'lineCap'; 1128 this.ctx[name] = attribute; 1129 return this; 1130 }, 1131 /** The lineJoin attribute defines the type of corners that UAs will place 1132 * where two lines meet. The three valid values are bevel, round, and miter. 1133 * 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> 1134 * @param {String} type 1135 * @returns this 1136 */ 1137 lineJoin: function (attribute){ 1138 var name = 'lineJoin'; 1139 this.ctx[name] = attribute; 1140 return this; 1141 }, 1142 1143 /** Sets the miter limit 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 {Number} number 1146 * @returns this 1147 */ 1148 miterLimit: function (attribute){ 1149 var name = 'miterLimit'; 1150 this.ctx[name] = attribute; 1151 return this; 1152 }, 1153 /** Sets the font property and takes the same syntax as setting the font property 1154 * in CSS 1155 * 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> 1156 * @param {String} 1157 * @returns this 1158 */ 1159 font: function (attribute){ 1160 var name = 'font'; 1161 this.ctx[name] = attribute; 1162 return this; 1163 }, 1164 1165 /** Changes the text alignment. The possible values are start, end, left, right, 1166 * and center. The default is start. Other values are ignored. 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 {string} alignment 1169 * @returns this 1170 */ 1171 textAlign: function (attribute){ 1172 var name = 'textAlign'; 1173 this.ctx[name] = attribute; 1174 return this; 1175 }, 1176 1177 /** Changes the baseline alignment. If the value is one of top, hanging, middle, 1178 * alphabetic, ideographic, or bottom, then the value must be changed to the new value. 1179 * 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> 1180 * @param {String} alignment 1181 * @returns this 1182 */ 1183 textBaseline: function (attribute){ 1184 var name = 'textBaseline'; 1185 this.ctx[name] = attribute; 1186 return this; 1187 }, 1188 1189 /** Save the current state of the context 1190 * 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> 1191 * @param 1192 * @returns this 1193 */ 1194 save : function ( ){ 1195 var name = 'save'; 1196 this.ctx[name].apply(this.ctx, arguments); 1197 return this; 1198 }, 1199 /** Restore the saved context 1200 * 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> 1201 * @param 1202 * @returns this 1203 */ 1204 restore : function ( ){ 1205 var name = 'restore'; 1206 this.ctx[name].apply(this.ctx, arguments); 1207 return this; 1208 }, 1209 /** Perform a scale transformation 1210 * 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> 1211 * @param 1212 * @returns this 1213 */ 1214 scale : function ( ){ 1215 var name = 'scale'; 1216 this.ctx[name].apply(this.ctx, arguments); 1217 return this; 1218 }, 1219 /** Perform a rotation transformation 1220 * 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> 1221 * @param 1222 * @returns this 1223 */ 1224 rotate : function ( ){ 1225 var name = 'rotate'; 1226 this.ctx[name].apply(this.ctx, arguments); 1227 return this; 1228 }, 1229 /** Performa a translation transformation 1230 * 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> 1231 * @param 1232 * @returns this 1233 */ 1234 translate : function ( ){ 1235 var name = 'translate'; 1236 this.ctx[name].apply(this.ctx, arguments); 1237 return this; 1238 }, 1239 1240 /** Transform the identity matrix 1241 * 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> 1242 * @param 1243 * @returns this 1244 */ 1245 transform : function ( ){ 1246 var name = 'transform'; 1247 this.ctx[name].apply(this.ctx, arguments); 1248 return this; 1249 }, 1250 /** Set the transform 1251 * 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> 1252 * @param 1253 * @returns this 1254 */ 1255 setTransform : function ( ){ 1256 var name = 'setTransform'; 1257 this.ctx[name].apply(this.ctx, arguments); 1258 return this; 1259 }, 1260 /** Clear a rectangular area 1261 * 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> 1262 * @param 1263 * @returns this 1264 */ 1265 clearRect : function ( ){ 1266 var name = 'clearRect'; 1267 this.ctx[name].apply(this.ctx, arguments); 1268 return this; 1269 }, 1270 /** Fill a rectangular area 1271 * 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> 1272 * @param 1273 * @returns this 1274 */ 1275 fillRect : function ( ){ 1276 var name = 'fillRect'; 1277 this.ctx[name].apply(this.ctx, arguments); 1278 return this; 1279 }, 1280 1281 /** Draw the outline of the rectangle 1282 * 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> 1283 * @param 1284 * @returns this 1285 */ 1286 strokeRect : function ( ){ 1287 var name = 'strokeRect'; 1288 this.ctx[name].apply(this.ctx, arguments); 1289 return this; 1290 }, 1291 /** Begin a path 1292 * 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> 1293 * @param 1294 * @returns this 1295 */ 1296 beginPath : function ( ){ 1297 var name = 'beginPath'; 1298 this.ctx[name].apply(this.ctx, arguments); 1299 return this; 1300 }, 1301 /** End a path 1302 * 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> 1303 * @param 1304 * @returns this 1305 */ 1306 closePath : function ( ){ 1307 var name = 'closePath'; 1308 this.ctx[name].apply(this.ctx, arguments); 1309 return this; 1310 }, 1311 /** Move to specified 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 moveTo : function ( ){ 1317 var name = 'moveTo'; 1318 this.ctx[name].apply(this.ctx, arguments); 1319 return this; 1320 }, 1321 1322 1323 /** Draw a line to the given coordinates 1324 * 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> 1325 * @param 1326 * @returns this 1327 */ 1328 lineTo : function ( ){ 1329 var name = 'lineTo'; 1330 this.ctx[name].apply(this.ctx, arguments); 1331 return this; 1332 }, 1333 1334 /** Draw a quadratic curve to given coordinates 1335 * 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> 1336 * @param 1337 * @returns this 1338 */ 1339 quadraticCurveTo : function ( ){ 1340 var name = 'quadraticCurveTo'; 1341 this.ctx[name].apply(this.ctx, arguments); 1342 return this; 1343 }, 1344 /** Draw a bezier curve to given coordinates 1345 * 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> 1346 * @param 1347 * @returns this 1348 */ 1349 bezierCurveTo : function ( ){ 1350 var name = 'bezierCurveTo'; 1351 this.ctx[name].apply(this.ctx, arguments); 1352 return this; 1353 }, 1354 /** Draw an arc to the given points 1355 * 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> 1356 * @param 1357 * @returns this 1358 */ 1359 arcTo : function ( ){ 1360 var name = 'arcTo'; 1361 this.ctx[name].apply(this.ctx, arguments); 1362 return this; 1363 }, 1364 /** Create an arc 1365 * 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> 1366 * @param 1367 * @returns this 1368 */ 1369 arc : function ( ){ 1370 var name = 'arc'; 1371 this.ctx[name].apply(this.ctx, arguments); 1372 return this; 1373 }, 1374 1375 /** Create a rectangle 1376 * 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> 1377 * @param 1378 * @returns this 1379 */ 1380 rect : function ( ){ 1381 var name = 'rect'; 1382 this.ctx[name].apply(this.ctx, arguments); 1383 return this; 1384 }, 1385 /** fill in the current subpaths with the current fillstyle 1386 * 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> 1387 * @param 1388 * @returns this 1389 */ 1390 fill : function ( ){ 1391 var name = 'fill'; 1392 this.ctx[name].apply(this.ctx, arguments); 1393 return this; 1394 }, 1395 /** Stroke the subpaths 1396 * 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> 1397 * @param 1398 * @returns this 1399 */ 1400 stroke : function ( ){ 1401 var name = 'stroke'; 1402 this.ctx[name].apply(this.ctx, arguments); 1403 return this; 1404 }, 1405 1406 /** description 1407 * 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> 1408 * @param 1409 * @returns this 1410 */ 1411 clip : function ( ){ 1412 var name = 'clip'; 1413 this.ctx[name].apply(this.ctx, arguments); 1414 return this; 1415 }, 1416 /** description 1417 * 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> 1418 * @param 1419 * @returns this 1420 */ 1421 fillText : function ( ){ 1422 var name = 'fillText'; 1423 this.ctx[name].apply(this.ctx, arguments); 1424 return this; 1425 }, 1426 /** description 1427 * 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> 1428 * @param 1429 * @returns this 1430 */ 1431 strokeText : function ( ){ 1432 var name = 'strokeText'; 1433 this.ctx[name].apply(this.ctx, arguments); 1434 return this; 1435 }, 1436 /** description 1437 * 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> 1438 * @param 1439 * @returns this 1440 */ 1441 measureText : function ( ){ 1442 var name = 'measureText'; 1443 this.ctx[name].apply(this.ctx, arguments); 1444 return this; 1445 }, 1446 /** description 1447 * 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> 1448 * @param 1449 * @returns this 1450 */ 1451 isPointInPath : function ( ){ 1452 var name = 'isPointInPath'; 1453 this.ctx[name].apply(this.ctx, arguments); 1454 return this; 1455 }, 1456 1457 /** Sets the stroke style 1458 * 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> 1459 * @param 1460 * @returns this 1461 */ 1462 strokeStyle: function (attribute){ 1463 var name = 'strokeStyle'; 1464 this.ctx[name] = attribute; 1465 return this; 1466 }, 1467 1468 /** Sets the fill style 1469 * 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> 1470 * @param 1471 * @returns this 1472 */ 1473 fillStyle: function (attribute){ 1474 var name = 'fillStyle'; 1475 this.ctx[name] = attribute; 1476 return this; 1477 }, 1478 /** description 1479 * 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> 1480 * @param 1481 * @returns this 1482 */ 1483 createLinearGradient : function ( ){ 1484 var name = 'createLinearGradient'; 1485 this.ctx[name].apply(this.ctx, arguments); 1486 return this; 1487 }, 1488 /** description 1489 * 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> 1490 * @param 1491 * @returns this 1492 */ 1493 createRadialGradient : function ( ){ 1494 var name = 'createRadialGradient'; 1495 this.ctx[name].apply(this.ctx, arguments); 1496 return this; 1497 }, 1498 /** description 1499 * 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> 1500 * @param 1501 * @returns this 1502 */ 1503 createPattern : function ( ){ 1504 var name = 'createPattern'; 1505 this.ctx[name].apply(this.ctx, arguments); 1506 return this; 1507 }, 1508 /** description 1509 * 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> 1510 * @param 1511 * @returns this 1512 */ 1513 shadowOffsetX: function (attribute){ 1514 var name = 'shadowOffsetX'; 1515 this.ctx[name] = attribute; 1516 return this; 1517 }, 1518 /** description 1519 * 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> 1520 * @param 1521 * @returns this 1522 */ 1523 shadowOffsetY: function (attribute){ 1524 var name = 'shadowOffsetY'; 1525 this.ctx[name] = attribute; 1526 return this; 1527 }, 1528 /** description 1529 * 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> 1530 * @param 1531 * @returns this 1532 */ 1533 shadowBlur: function (attribute){ 1534 var name = 'shadowBlur'; 1535 this.ctx[name] = attribute; 1536 return this; 1537 }, 1538 /** description 1539 * 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> 1540 * @param 1541 * @returns this 1542 */ 1543 shadowColor: function (attribute){ 1544 var name = 'shadowColor'; 1545 this.ctx[name] = attribute; 1546 return this; 1547 }, 1548 /** description 1549 * 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> 1550 * @param 1551 * @returns this 1552 */ 1553 drawImage : function ( ){ 1554 var name = 'drawImage'; 1555 this.ctx[name].apply(this.ctx, arguments); 1556 return this; 1557 }, 1558 /** description 1559 * 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> 1560 * @param 1561 * @returns this 1562 */ 1563 getImageData : function ( ){ 1564 var name = 'getImageData'; 1565 this.ctx[name].apply(this.ctx, arguments); 1566 return this; 1567 }, 1568 /** description 1569 * 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> 1570 * @param 1571 * @returns this 1572 */ 1573 putImageData : function ( ){ 1574 var name = 'putImageData'; 1575 this.ctx[name].apply(this.ctx, arguments); 1576 return this; 1577 }, 1578 /** description 1579 * 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> 1580 * @param 1581 * @returns this 1582 */ 1583 createImageData : function ( ){ 1584 var name = 'createImageData'; 1585 this.ctx[name].apply(this.ctx, arguments); 1586 return this; 1587 }, 1588 /** description 1589 * 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> 1590 * @param 1591 * @returns this 1592 */ 1593 drawWindow : function ( ){ 1594 var name = 'drawWindow'; 1595 this.ctx[name].apply(this.ctx, arguments); 1596 return this; 1597 }, 1598 1599 1600 1601 1602 }; 1603 1604 1605 /** Prototype object for each svg element submitted to Karma in the 1606 * Karma() method 1607 * @throws {Error} if the name and domId for the svg element are not specified 1608 * @thows {Error} if the supplied domId does not match an element in the DOM 1609 * @class This object is the prototype for each svg element submitted to Karma in the 1610 * Karma() method 1611 */ 1612 Karma.kSvg = { 1613 /** name of instance, used internally 1614 * @typeof string 1615 * @default "" 1616 */ 1617 name : "", 1618 /** width of element 1619 * @type number 1620 * @default 0 1621 */ 1622 width: 0, 1623 /** height of element 1624 * @type number 1625 * @default 0 1626 */ 1627 height: 0, 1628 /** Status of element, either "loaded" or "error" 1629 * @type string 1630 * @default "" 1631 */ 1632 status: "", 1633 /** Whether canvas is visible. This value is read-only 1634 * @type boolean 1635 * @default true 1636 */ 1637 visible: true, 1638 /** Element ID for canvas element in html document. 1639 * @type String 1640 * @default undefined 1641 */ 1642 domId: undefined, 1643 /** Reference to the DOM element. 1644 * @type DOMElement 1645 * @default undefined 1646 * @example 1647 * //You can access all properties and methods of the underlying DOM element 1648 * //using the 'node' property 1649 * Karma.svg.someSvg.node.dispatchEvent; 1650 * Karma.svg.someSvg.node.addEvenListener(...); 1651 */ 1652 node: undefined, 1653 /** Reference to the SVGDocument. You can use the this.doc to manipulate 1654 * the SVG document 1655 * @type SVGDocument 1656 * @default undefined 1657 * @example 1658 * var myElem = Karma.svg.someSvg.doc.getElementById('foobar'); 1659 * Karma.svg.someSvg.doc.createElement(...); 1660 * Karma.svg.someSvg.doc.removeChild(someNode); 1661 * 1662 */ 1663 doc: undefined, 1664 /** Reference to the root element of the SVG Document 1665 * @type DocumentElement 1666 * @default undefined 1667 * @example 1668 * // The root element is equivalent to "document" in a regular html document 1669 * // The root attribute is used frequently with the jQuery SVG plugin for CSS selectors 1670 * $('#someId', Karma.svg.someSvg.root).css(.. manipulate css attributes ...); 1671 */ 1672 root: undefined, 1673 _localized : undefined, 1674 _init: function (config) { 1675 Karma._counters.total++; 1676 1677 for (var option in config){ 1678 if (config.hasOwnProperty(option)){ 1679 switch (option){ 1680 case "name": 1681 this.name = config[option]; 1682 break; 1683 case "domId": 1684 this.domId = config[option]; 1685 break; 1686 case "width": 1687 if(!this.height){ 1688 throw new Error("If you specify a width you must also" + 1689 "specify a height"); 1690 } 1691 this.width = parseInt(config[option], 10); 1692 break; 1693 case "height": 1694 if(!this.width){ 1695 throw new Error("If you specify a height you must also" + 1696 "specify a width"); 1697 } 1698 this.height = config[option]; 1699 break; 1700 } 1701 } 1702 } 1703 1704 if(this.domId && document.getElementById(this.domId)){ 1705 this.node = document.getElementById(this.domId); 1706 } else { 1707 throw new Error('you must specify a valid domId that' + 1708 'is in your html page'); 1709 } 1710 1711 if(!config.height && !config.width){ 1712 this.width = parseInt(this.node.getAttribute('width'), 10); 1713 this.height = parseInt(this.node.getAttribute('height'), 10); 1714 } 1715 1716 var that = this; 1717 that._addEventHandlers(); 1718 1719 return this; 1720 1721 1722 }, 1723 _addEventHandlers : function () { 1724 var that = this; 1725 that.doc = that.node.getSVGDocument(); 1726 that.node.addEventListener( 1727 "load", 1728 function (e) { 1729 that.doc = that.node.getSVGDocument(); 1730 that.root = that.doc.documentElement; 1731 Karma._counters.loaded++; 1732 Karma._updateStatus(); 1733 that.status = "loaded"; 1734 }, false); 1735 1736 that.node.addEventListener( 1737 "error", 1738 function (e) { 1739 Karma._counters.loaded--; 1740 Karma._counters.errors++; 1741 that.status = "error"; 1742 var errorMsg = "Error: " + that._type.toUpperCase() + 1743 " " + that.name + " cannot be loaded."; 1744 Karma._updateStatus(errorMsg); 1745 }, 1746 false); 1747 that.node.addEventListener( 1748 "abort", 1749 function (e) { 1750 that.status = "aborted"; 1751 var errorMsg = "ABORT: " + that._type.toUpperCase() + 1752 " " + that.name + " loading was aborted."; 1753 Karma._updateStatus(errorMsg); 1754 1755 }, false); 1756 1757 } 1758 }; 1759