1/** vim: et:ts=4:sw=4:sts=4 2 * @license RequireJS 2.1.5 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. 3 * Available via the MIT or new BSD license. 4 * see: http://github.com/jrburke/requirejs for details 5 */ 6//Not using strict: uneven strict support in browsers, #392, and causes 7//problems with requirejs.exec()/transpiler plugins that may not be strict. 8/*jslint regexp: true, nomen: true, sloppy: true */ 9/*global window, navigator, document, importScripts, setTimeout, opera */ 10 11var requirejs, require, define; 12(function (global) { 13 var req, s, head, baseElement, dataMain, src, 14 interactiveScript, currentlyAddingScript, mainScript, subPath, 15 version = '2.1.5', 16 commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg, 17 cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g, 18 jsSuffixRegExp = /\.js$/, 19 currDirRegExp = /^\.\//, 20 op = Object.prototype, 21 ostring = op.toString, 22 hasOwn = op.hasOwnProperty, 23 ap = Array.prototype, 24 apsp = ap.splice, 25 isBrowser = !!(typeof window !== 'undefined' && navigator && document), 26 isWebWorker = !isBrowser && typeof importScripts !== 'undefined', 27 //PS3 indicates loaded and complete, but need to wait for complete 28 //specifically. Sequence is 'loading', 'loaded', execution, 29 // then 'complete'. The UA check is unfortunate, but not sure how 30 //to feature test w/o causing perf issues. 31 readyRegExp = isBrowser && navigator.platform === 'PLAYSTATION 3' ? 32 /^complete$/ : /^(complete|loaded)$/, 33 defContextName = '_', 34 //Oh the tragedy, detecting opera. See the usage of isOpera for reason. 35 isOpera = typeof opera !== 'undefined' && opera.toString() === '[object Opera]', 36 contexts = {}, 37 cfg = {}, 38 globalDefQueue = [], 39 useInteractive = false; 40 41 function isFunction(it) { 42 return ostring.call(it) === '[object Function]'; 43 } 44 45 function isArray(it) { 46 return ostring.call(it) === '[object Array]'; 47 } 48 49 /** 50 * Helper function for iterating over an array. If the func returns 51 * a true value, it will break out of the loop. 52 */ 53 function each(ary, func) { 54 if (ary) { 55 var i; 56 for (i = 0; i < ary.length; i += 1) { 57 if (ary[i] && func(ary[i], i, ary)) { 58 break; 59 } 60 } 61 } 62 } 63 64 /** 65 * Helper function for iterating over an array backwards. If the func 66 * returns a true value, it will break out of the loop. 67 */ 68 function eachReverse(ary, func) { 69 if (ary) { 70 var i; 71 for (i = ary.length - 1; i > -1; i -= 1) { 72 if (ary[i] && func(ary[i], i, ary)) { 73 break; 74 } 75 } 76 } 77 } 78 79 function hasProp(obj, prop) { 80 return hasOwn.call(obj, prop); 81 } 82 83 function getOwn(obj, prop) { 84 return hasProp(obj, prop) && obj[prop]; 85 } 86 87 /** 88 * Cycles over properties in an object and calls a function for each 89 * property value. If the function returns a truthy value, then the 90 * iteration is stopped. 91 */ 92 function eachProp(obj, func) { 93 var prop; 94 for (prop in obj) { 95 if (hasProp(obj, prop)) { 96 if (func(obj[prop], prop)) { 97 break; 98 } 99 } 100 } 101 } 102 103 /** 104 * Simple function to mix in properties from source into target, 105 * but only if target does not already have a property of the same name. 106 */ 107 function mixin(target, source, force, deepStringMixin) { 108 if (source) { 109 eachProp(source, function (value, prop) { 110 if (force || !hasProp(target, prop)) { 111 if (deepStringMixin && typeof value !== 'string') { 112 if (!target[prop]) { 113 target[prop] = {}; 114 } 115 mixin(target[prop], value, force, deepStringMixin); 116 } else { 117 target[prop] = value; 118 } 119 } 120 }); 121 } 122 return target; 123 } 124 125 //Similar to Function.prototype.bind, but the 'this' object is specified 126 //first, since it is easier to read/figure out what 'this' will be. 127 function bind(obj, fn) { 128 return function () { 129 return fn.apply(obj, arguments); 130 }; 131 } 132 133 function scripts() { 134 return document.getElementsByTagName('script'); 135 } 136 137 //Allow getting a global that expressed in 138 //dot notation, like 'a.b.c'. 139 function getGlobal(value) { 140 if (!value) { 141 return value; 142 } 143 var g = global; 144 each(value.split('.'), function (part) { 145 g = g[part]; 146 }); 147 return g; 148 } 149 150 /** 151 * Constructs an error with a pointer to an URL with more information. 152 * @param {String} id the error ID that maps to an ID on a web page. 153 * @param {String} message human readable error. 154 * @param {Error} [err] the original error, if there is one. 155 * 156 * @returns {Error} 157 */ 158 function makeError(id, msg, err, requireModules) { 159 var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id); 160 e.requireType = id; 161 e.requireModules = requireModules; 162 if (err) { 163 e.originalError = err; 164 } 165 return e; 166 } 167 168 if (typeof define !== 'undefined') { 169 //If a define is already in play via another AMD loader, 170 //do not overwrite. 171 return; 172 } 173 174 if (typeof requirejs !== 'undefined') { 175 if (isFunction(requirejs)) { 176 //Do not overwrite and existing requirejs instance. 177 return; 178 } 179 cfg = requirejs; 180 requirejs = undefined; 181 } 182 183 //Allow for a require config object 184 if (typeof require !== 'undefined' && !isFunction(require)) { 185 //assume it is a config object. 186 cfg = require; 187 require = undefined; 188 } 189 190 function newContext(contextName) { 191 var inCheckLoaded, Module, context, handlers, 192 checkLoadedTimeoutId, 193 config = { 194 //Defaults. Do not set a default for map 195 //config to speed up normalize(), which 196 //will run faster if there is no default. 197 waitSeconds: 7, 198 baseUrl: './', 199 paths: {}, 200 pkgs: {}, 201 shim: {}, 202 config: {} 203 }, 204 registry = {}, 205 //registry of just enabled modules, to speed 206 //cycle breaking code when lots of modules 207 //are registered, but not activated. 208 enabledRegistry = {}, 209 undefEvents = {}, 210 defQueue = [], 211 defined = {}, 212 urlFetched = {}, 213 requireCounter = 1, 214 unnormalizedCounter = 1; 215 216 /** 217 * Trims the . and .. from an array of path segments. 218 * It will keep a leading path segment if a .. will become 219 * the first path segment, to help with module name lookups, 220 * which act like paths, but can be remapped. But the end result, 221 * all paths that use this function should look normalized. 222 * NOTE: this method MODIFIES the input array. 223 * @param {Array} ary the array of path segments. 224 */ 225 function trimDots(ary) { 226 var i, part; 227 for (i = 0; ary[i]; i += 1) { 228 part = ary[i]; 229 if (part === '.') { 230 ary.splice(i, 1); 231 i -= 1; 232 } else if (part === '..') { 233 if (i === 1 && (ary[2] === '..' || ary[0] === '..')) { 234 //End of the line. Keep at least one non-dot 235 //path segment at the front so it can be mapped 236 //correctly to disk. Otherwise, there is likely 237 //no path mapping for a path starting with '..'. 238 //This can still fail, but catches the most reasonable 239 //uses of .. 240 break; 241 } else if (i > 0) { 242 ary.splice(i - 1, 2); 243 i -= 2; 244 } 245 } 246 } 247 } 248 249 /** 250 * Given a relative module name, like ./something, normalize it to 251 * a real name that can be mapped to a path. 252 * @param {String} name the relative name 253 * @param {String} baseName a real name that the name arg is relative 254 * to. 255 * @param {Boolean} applyMap apply the map config to the value. Should 256 * only be done if this normalization is for a dependency ID. 257 * @returns {String} normalized name 258 */ 259 function normalize(name, baseName, applyMap) { 260 var pkgName, pkgConfig, mapValue, nameParts, i, j, nameSegment, 261 foundMap, foundI, foundStarMap, starI, 262 baseParts = baseName && baseName.split('/'), 263 normalizedBaseParts = baseParts, 264 map = config.map, 265 starMap = map && map['*']; 266 267 //Adjust any relative paths. 268 if (name && name.charAt(0) === '.') { 269 //If have a base name, try to normalize against it, 270 //otherwise, assume it is a top-level require that will 271 //be relative to baseUrl in the end. 272 if (baseName) { 273 if (getOwn(config.pkgs, baseName)) { 274 //If the baseName is a package name, then just treat it as one 275 //name to concat the name with. 276 normalizedBaseParts = baseParts = [baseName]; 277 } else { 278 //Convert baseName to array, and lop off the last part, 279 //so that . matches that 'directory' and not name of the baseName's 280 //module. For instance, baseName of 'one/two/three', maps to 281 //'one/two/three.js', but we want the directory, 'one/two' for 282 //this normalization. 283 normalizedBaseParts = baseParts.slice(0, baseParts.length - 1); 284 } 285 286 name = normalizedBaseParts.concat(name.split('/')); 287 trimDots(name); 288 289 //Some use of packages may use a . path to reference the 290 //'main' module name, so normalize for that. 291 pkgConfig = getOwn(config.pkgs, (pkgName = name[0])); 292 name = name.join('/'); 293 if (pkgConfig && name === pkgName + '/' + pkgConfig.main) { 294 name = pkgName; 295 } 296 } else if (name.indexOf('./') === 0) { 297 // No baseName, so this is ID is resolved relative 298 // to baseUrl, pull off the leading dot. 299 name = name.substring(2); 300 } 301 } 302 303 //Apply map config if available. 304 if (applyMap && map && (baseParts || starMap)) { 305 nameParts = name.split('/'); 306 307 for (i = nameParts.length; i > 0; i -= 1) { 308 nameSegment = nameParts.slice(0, i).join('/'); 309 310 if (baseParts) { 311 //Find the longest baseName segment match in the config. 312 //So, do joins on the biggest to smallest lengths of baseParts. 313 for (j = baseParts.length; j > 0; j -= 1) { 314 mapValue = getOwn(map, baseParts.slice(0, j).join('/')); 315 316 //baseName segment has config, find if it has one for 317 //this name. 318 if (mapValue) { 319 mapValue = getOwn(mapValue, nameSegment); 320 if (mapValue) { 321 //Match, update name to the new value. 322 foundMap = mapValue; 323 foundI = i; 324 break; 325 } 326 } 327 } 328 } 329 330 if (foundMap) { 331 break; 332 } 333 334 //Check for a star map match, but just hold on to it, 335 //if there is a shorter segment match later in a matching 336 //config, then favor over this star map. 337 if (!foundStarMap && starMap && getOwn(starMap, nameSegment)) { 338 foundStarMap = getOwn(starMap, nameSegment); 339 starI = i; 340 } 341 } 342 343 if (!foundMap && foundStarMap) { 344 foundMap = foundStarMap; 345 foundI = starI; 346 } 347 348 if (foundMap) { 349 nameParts.splice(0, foundI, foundMap); 350 name = nameParts.join('/'); 351 } 352 } 353 354 return name; 355 } 356 357 function removeScript(name) { 358 if (isBrowser) { 359 each(scripts(), function (scriptNode) { 360 if (scriptNode.getAttribute('data-requiremodule') === name && 361 scriptNode.getAttribute('data-requirecontext') === context.contextName) { 362 scriptNode.parentNode.removeChild(scriptNode); 363 return true; 364 } 365 }); 366 } 367 } 368 369 function hasPathFallback(id) { 370 var pathConfig = getOwn(config.paths, id); 371 if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) { 372 removeScript(id); 373 //Pop off the first array value, since it failed, and 374 //retry 375 pathConfig.shift(); 376 context.require.undef(id); 377 context.require([id]); 378 return true; 379 } 380 } 381 382 //Turns a plugin!resource to [plugin, resource] 383 //with the plugin being undefined if the name 384 //did not have a plugin prefix. 385 function splitPrefix(name) { 386 var prefix, 387 index = name ? name.indexOf('!') : -1; 388 if (index > -1) { 389 prefix = name.substring(0, index); 390 name = name.substring(index + 1, name.length); 391 } 392 return [prefix, name]; 393 } 394 395 /** 396 * Creates a module mapping that includes plugin prefix, module 397 * name, and path. If parentModuleMap is provided it will 398 * also normalize the name via require.normalize() 399 * 400 * @param {String} name the module name 401 * @param {String} [parentModuleMap] parent module map 402 * for the module name, used to resolve relative names. 403 * @param {Boolean} isNormalized: is the ID already normalized. 404 * This is true if this call is done for a define() module ID. 405 * @param {Boolean} applyMap: apply the map config to the ID. 406 * Should only be true if this map is for a dependency. 407 * 408 * @returns {Object} 409 */ 410 function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) { 411 var url, pluginModule, suffix, nameParts, 412 prefix = null, 413 parentName = parentModuleMap ? parentModuleMap.name : null, 414 originalName = name, 415 isDefine = true, 416 normalizedName = ''; 417 418 //If no name, then it means it is a require call, generate an 419 //internal name. 420 if (!name) { 421 isDefine = false; 422 name = '_@r' + (requireCounter += 1); 423 } 424 425 nameParts = splitPrefix(name); 426 prefix = nameParts[0]; 427 name = nameParts[1]; 428 429 if (prefix) { 430 prefix = normalize(prefix, parentName, applyMap); 431 pluginModule = getOwn(defined, prefix); 432 } 433 434 //Account for relative paths if there is a base name. 435 if (name) { 436 if (prefix) { 437 if (pluginModule && pluginModule.normalize) { 438 //Plugin is loaded, use its normalize method. 439 normalizedName = pluginModule.normalize(name, function (name) { 440 return normalize(name, parentName, applyMap); 441 }); 442 } else { 443 normalizedName = normalize(name, parentName, applyMap); 444 } 445 } else { 446 //A regular module. 447 normalizedName = normalize(name, parentName, applyMap); 448 449 //Normalized name may be a plugin ID due to map config 450 //application in normalize. The map config values must 451 //already be normalized, so do not need to redo that part. 452 nameParts = splitPrefix(normalizedName); 453 prefix = nameParts[0]; 454 normalizedName = nameParts[1]; 455 isNormalized = true; 456 457 url = context.nameToUrl(normalizedName); 458 } 459 } 460 461 //If the id is a plugin id that cannot be determined if it needs 462 //normalization, stamp it with a unique ID so two matching relative 463 //ids that may conflict can be separate. 464 suffix = prefix && !pluginModule && !isNormalized ? 465 '_unnormalized' + (unnormalizedCounter += 1) : 466 ''; 467 468 return { 469 prefix: prefix, 470 name: normalizedName, 471 parentMap: parentModuleMap, 472 unnormalized: !!suffix, 473 url: url, 474 originalName: originalName, 475 isDefine: isDefine, 476 id: (prefix ? 477 prefix + '!' + normalizedName : 478 normalizedName) + suffix 479 }; 480 } 481 482 function getModule(depMap) { 483 var id = depMap.id, 484 mod = getOwn(registry, id); 485 486 if (!mod) { 487 mod = registry[id] = new context.Module(depMap); 488 } 489 490 return mod; 491 } 492 493 function on(depMap, name, fn) { 494 var id = depMap.id, 495 mod = getOwn(registry, id); 496 497 if (hasProp(defined, id) && 498 (!mod || mod.defineEmitComplete)) { 499 if (name === 'defined') { 500 fn(defined[id]); 501 } 502 } else { 503 getModule(depMap).on(name, fn); 504 } 505 } 506 507 function onError(err, errback) { 508 var ids = err.requireModules, 509 notified = false; 510 511 if (errback) { 512 errback(err); 513 } else { 514 each(ids, function (id) { 515 var mod = getOwn(registry, id); 516 if (mod) { 517 //Set error on module, so it skips timeout checks. 518 mod.error = err; 519 if (mod.events.error) { 520 notified = true; 521 mod.emit('error', err); 522 } 523 } 524 }); 525 526 if (!notified) { 527 req.onError(err); 528 } 529 } 530 } 531 532 /** 533 * Internal method to transfer globalQueue items to this context's 534 * defQueue. 535 */ 536 function takeGlobalQueue() { 537 //Push all the globalDefQueue items into the context's defQueue 538 if (globalDefQueue.length) { 539 //Array splice in the values since the context code has a 540 //local var ref to defQueue, so cannot just reassign the one 541 //on context. 542 apsp.apply(defQueue, 543 [defQueue.length - 1, 0].concat(globalDefQueue)); 544 globalDefQueue = []; 545 } 546 } 547 548 handlers = { 549 'require': function (mod) { 550 if (mod.require) { 551 return mod.require; 552 } else { 553 return (mod.require = context.makeRequire(mod.map)); 554 } 555 }, 556 'exports': function (mod) { 557 mod.usingExports = true; 558 if (mod.map.isDefine) { 559 if (mod.exports) { 560 return mod.exports; 561 } else { 562 return (mod.exports = defined[mod.map.id] = {}); 563 } 564 } 565 }, 566 'module': function (mod) { 567 if (mod.module) { 568 return mod.module; 569 } else { 570 return (mod.module = { 571 id: mod.map.id, 572 uri: mod.map.url, 573 config: function () { 574 return (config.config && getOwn(config.config, mod.map.id)) || {}; 575 }, 576 exports: defined[mod.map.id] 577 }); 578 } 579 } 580 }; 581 582 function cleanRegistry(id) { 583 //Clean up machinery used for waiting modules. 584 delete registry[id]; 585 delete enabledRegistry[id]; 586 } 587 588 function breakCycle(mod, traced, processed) { 589 var id = mod.map.id; 590 591 if (mod.error) { 592 mod.emit('error', mod.error); 593 } else { 594 traced[id] = true; 595 each(mod.depMaps, function (depMap, i) { 596 var depId = depMap.id, 597 dep = getOwn(registry, depId); 598 599 //Only force things that have not completed 600 //being defined, so still in the registry, 601 //and only if it has not been matched up 602 //in the module already. 603 if (dep && !mod.depMatched[i] && !processed[depId]) { 604 if (getOwn(traced, depId)) { 605 mod.defineDep(i, defined[depId]); 606 mod.check(); //pass false? 607 } else { 608 breakCycle(dep, traced, processed); 609 } 610 } 611 }); 612 processed[id] = true; 613 } 614 } 615 616 function checkLoaded() { 617 var map, modId, err, usingPathFallback, 618 waitInterval = config.waitSeconds * 1000, 619 //It is possible to disable the wait interval by using waitSeconds of 0. 620 expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(), 621 noLoads = [], 622 reqCalls = [], 623 stillLoading = false, 624 needCycleCheck = true; 625 626 //Do not bother if this call was a result of a cycle break. 627 if (inCheckLoaded) { 628 return; 629 } 630 631 inCheckLoaded = true; 632 633 //Figure out the state of all the modules. 634 eachProp(enabledRegistry, function (mod) { 635 map = mod.map; 636 modId = map.id; 637 638 //Skip things that are not enabled or in error state. 639 if (!mod.enabled) { 640 return; 641 } 642 643 if (!map.isDefine) { 644 reqCalls.push(mod); 645 } 646 647 if (!mod.error) { 648 //If the module should be executed, and it has not 649 //been inited and time is up, remember it. 650 if (!mod.inited && expired) { 651 if (hasPathFallback(modId)) { 652 usingPathFallback = true; 653 stillLoading = true; 654 } else { 655 noLoads.push(modId); 656 removeScript(modId); 657 } 658 } else if (!mod.inited && mod.fetched && map.isDefine) { 659 stillLoading = true; 660 if (!map.prefix) { 661 //No reason to keep looking for unfinished 662 //loading. If the only stillLoading is a 663 //plugin resource though, keep going, 664 //because it may be that a plugin resource 665 //is waiting on a non-plugin cycle. 666 return (needCycleCheck = false); 667 } 668 } 669 } 670 }); 671 672 if (expired && noLoads.length) { 673 //If wait time expired, throw error of unloaded modules. 674 err = makeError('timeout', 'Load timeout for modules: ' + noLoads, null, noLoads); 675 err.contextName = context.contextName; 676 return onError(err); 677 } 678 679 //Not expired, check for a cycle. 680 if (needCycleCheck) { 681 each(reqCalls, function (mod) { 682 breakCycle(mod, {}, {}); 683 }); 684 } 685 686 //If still waiting on loads, and the waiting load is something 687 //other than a plugin resource, or there are still outstanding 688 //scripts, then just try back later. 689 if ((!expired || usingPathFallback) && stillLoading) { 690 //Something is still waiting to load. Wait for it, but only 691 //if a timeout is not already in effect. 692 if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) { 693 checkLoadedTimeoutId = setTimeout(function () { 694 checkLoadedTimeoutId = 0; 695 checkLoaded(); 696 }, 50); 697 } 698 } 699 700 inCheckLoaded = false; 701 } 702 703 Module = function (map) { 704 this.events = getOwn(undefEvents, map.id) || {}; 705 this.map = map; 706 this.shim = getOwn(config.shim, map.id); 707 this.depExports = []; 708 this.depMaps = []; 709 this.depMatched = []; 710 this.pluginMaps = {}; 711 this.depCount = 0; 712 713 /* this.exports this.factory 714 this.depMaps = [], 715 this.enabled, this.fetched 716 */ 717 }; 718 719 Module.prototype = { 720 init: function (depMaps, factory, errback, options) { 721 options = options || {}; 722 723 //Do not do more inits if already done. Can happen if there 724 //are multiple define calls for the same module. That is not 725 //a normal, common case, but it is also not unexpected. 726 if (this.inited) { 727 return; 728 } 729 730 this.factory = factory; 731 732 if (errback) { 733 //Register for errors on this module. 734 this.on('error', errback); 735 } else if (this.events.error) { 736 //If no errback already, but there are error listeners 737 //on this module, set up an errback to pass to the deps. 738 errback = bind(this, function (err) { 739 this.emit('error', err); 740 }); 741 } 742 743 //Do a copy of the dependency array, so that 744 //source inputs are not modified. For example 745 //"shim" deps are passed in here directly, and 746 //doing a direct modification of the depMaps array 747 //would affect that config. 748 this.depMaps = depMaps && depMaps.slice(0); 749 750 this.errback = errback; 751 752 //Indicate this module has be initialized 753 this.inited = true; 754 755 this.ignore = options.ignore; 756 757 //Could have option to init this module in enabled mode, 758 //or could have been previously marked as enabled. However, 759 //the dependencies are not known until init is called. So 760 //if enabled previously, now trigger dependencies as enabled. 761 if (options.enabled || this.enabled) { 762 //Enable this module and dependencies. 763 //Will call this.check() 764 this.enable(); 765 } else { 766 this.check(); 767 } 768 }, 769 770 defineDep: function (i, depExports) { 771 //Because of cycles, defined callback for a given 772 //export can be called more than once. 773 if (!this.depMatched[i]) { 774 this.depMatched[i] = true; 775 this.depCount -= 1; 776 this.depExports[i] = depExports; 777 } 778 }, 779 780 fetch: function () { 781 if (this.fetched) { 782 return; 783 } 784 this.fetched = true; 785 786 context.startTime = (new Date()).getTime(); 787 788 var map = this.map; 789 790 //If the manager is for a plugin managed resource, 791 //ask the plugin to load it now. 792 if (this.shim) { 793 context.makeRequire(this.map, { 794 enableBuildCallback: true 795 })(this.shim.deps || [], bind(this, function () { 796 return map.prefix ? this.callPlugin() : this.load(); 797 })); 798 } else { 799 //Regular dependency. 800 return map.prefix ? this.callPlugin() : this.load(); 801 } 802 }, 803 804 load: function () { 805 var url = this.map.url; 806 807 //Regular dependency. 808 if (!urlFetched[url]) { 809 urlFetched[url] = true; 810 context.load(this.map.id, url); 811 } 812 }, 813 814 /** 815 * Checks if the module is ready to define itself, and if so, 816 * define it. 817 */ 818 check: function () { 819 if (!this.enabled || this.enabling) { 820 return; 821 } 822 823 var err, cjsModule, 824 id = this.map.id, 825 depExports = this.depExports, 826 exports = this.exports, 827 factory = this.factory; 828 829 if (!this.inited) { 830 this.fetch(); 831 } else if (this.error) { 832 this.emit('error', this.error); 833 } else if (!this.defining) { 834 //The factory could trigger another require call 835 //that would result in checking this module to 836 //define itself again. If already in the process 837 //of doing that, skip this work. 838 this.defining = true; 839 840 if (this.depCount < 1 && !this.defined) { 841 if (isFunction(factory)) { 842 //If there is an error listener, favor passing 843 //to that instead of throwing an error. 844 if (this.events.error) { 845 try { 846 exports = context.execCb(id, factory, depExports, exports); 847 } catch (e) { 848 err = e; 849 } 850 } else { 851 exports = context.execCb(id, factory, depExports, exports); 852 } 853 854 if (this.map.isDefine) { 855 //If setting exports via 'module' is in play, 856 //favor that over return value and exports. After that, 857 //favor a non-undefined return value over exports use. 858 cjsModule = this.module; 859 if (cjsModule && 860 cjsModule.exports !== undefined && 861 //Make sure it is not already the exports value 862 cjsModule.exports !== this.exports) { 863 exports = cjsModule.exports; 864 } else if (exports === undefined && this.usingExports) { 865 //exports already set the defined value. 866 exports = this.exports; 867 } 868 } 869 870 if (err) { 871 err.requireMap = this.map; 872 err.requireModules = [this.map.id]; 873 err.requireType = 'define'; 874 return onError((this.error = err)); 875 } 876 877 } else { 878 //Just a literal value 879 exports = factory; 880 } 881 882 this.exports = exports; 883 884 if (this.map.isDefine && !this.ignore) { 885 defined[id] = exports; 886 887 if (req.onResourceLoad) { 888 req.onResourceLoad(context, this.map, this.depMaps); 889 } 890 } 891 892 //Clean up 893 cleanRegistry(id); 894 895 this.defined = true; 896 } 897 898 //Finished the define stage. Allow calling check again 899 //to allow define notifications below in the case of a 900 //cycle. 901 this.defining = false; 902 903 if (this.defined && !this.defineEmitted) { 904 this.defineEmitted = true; 905 this.emit('defined', this.exports); 906 this.defineEmitComplete = true; 907 } 908 909 } 910 }, 911 912 callPlugin: function () { 913 var map = this.map, 914 id = map.id, 915 //Map already normalized the prefix. 916 pluginMap = makeModuleMap(map.prefix); 917 918 //Mark this as a dependency for this plugin, so it 919 //can be traced for cycles. 920 this.depMaps.push(pluginMap); 921 922 on(pluginMap, 'defined', bind(this, function (plugin) { 923 var load, normalizedMap, normalizedMod, 924 name = this.map.name, 925 parentName = this.map.parentMap ? this.map.parentMap.name : null, 926 localRequire = context.makeRequire(map.parentMap, { 927 enableBuildCallback: true 928 }); 929 930 //If current map is not normalized, wait for that 931 //normalized name to load instead of continuing. 932 if (this.map.unnormalized) { 933 //Normalize the ID if the plugin allows it. 934 if (plugin.normalize) { 935 name = plugin.normalize(name, function (name) { 936 return normalize(name, parentName, true); 937 }) || ''; 938 } 939 940 //prefix and name should already be normalized, no need 941 //for applying map config again either. 942 normalizedMap = makeModuleMap(map.prefix + '!' + name, 943 this.map.parentMap); 944 on(normalizedMap, 945 'defined', bind(this, function (value) { 946 this.init([], function () { return value; }, null, { 947 enabled: true, 948 ignore: true 949 }); 950 })); 951 952 normalizedMod = getOwn(registry, normalizedMap.id); 953 if (normalizedMod) { 954 //Mark this as a dependency for this plugin, so it 955 //can be traced for cycles. 956 this.depMaps.push(normalizedMap); 957 958 if (this.events.error) { 959 normalizedMod.on('error', bind(this, function (err) { 960 this.emit('error', err); 961 })); 962 } 963 normalizedMod.enable(); 964 } 965 966 return; 967 } 968 969 load = bind(this, function (value) { 970 this.init([], function () { return value; }, null, { 971 enabled: true 972 }); 973 }); 974 975 load.error = bind(this, function (err) { 976 this.inited = true; 977 this.error = err; 978 err.requireModules = [id]; 979 980 //Remove temp unnormalized modules for this module, 981 //since they will never be resolved otherwise now. 982 eachProp(registry, function (mod) { 983 if (mod.map.id.indexOf(id + '_unnormalized') === 0) { 984 cleanRegistry(mod.map.id); 985 } 986 }); 987 988 onError(err); 989 }); 990 991 //Allow plugins to load other code without having to know the 992 //context or how to 'complete' the load. 993 load.fromText = bind(this, function (text, textAlt) { 994 /*jslint evil: true */ 995 var moduleName = map.name, 996 moduleMap = makeModuleMap(moduleName), 997 hasInteractive = useInteractive; 998 999 //As of 2.1.0, support just passing the text, to reinforce 1000 //fromText only being called once per resource. Still 1001 //support old style of passing moduleName but discard 1002 //that moduleName in favor of the internal ref. 1003 if (textAlt) { 1004 text = textAlt; 1005 } 1006 1007 //Turn off interactive script matching for IE for any define 1008 //calls in the text, then turn it back on at the end. 1009 if (hasInteractive) { 1010 useInteractive = false; 1011 } 1012 1013 //Prime the system by creating a module instance for 1014 //it. 1015 getModule(moduleMap); 1016 1017 //Transfer any config to this other module. 1018 if (hasProp(config.config, id)) { 1019 config.config[moduleName] = config.config[id]; 1020 } 1021 1022 try { 1023 req.exec(text); 1024 } catch (e) { 1025 return onError(makeError('fromtexteval', 1026 'fromText eval for ' + id + 1027 ' failed: ' + e, 1028 e, 1029 [id])); 1030 } 1031 1032 if (hasInteractive) { 1033 useInteractive = true; 1034 } 1035 1036 //Mark this as a dependency for the plugin 1037 //resource 1038 this.depMaps.push(moduleMap); 1039 1040 //Support anonymous modules. 1041 context.completeLoad(moduleName); 1042 1043 //Bind the value of that module to the value for this 1044 //resource ID. 1045 localRequire([moduleName], load); 1046 }); 1047 1048 //Use parentName here since the plugin's name is not reliable, 1049 //could be some weird string with no path that actually wants to 1050 //reference the parentName's path. 1051 plugin.load(map.name, localRequire, load, config); 1052 })); 1053 1054 context.enable(pluginMap, this); 1055 this.pluginMaps[pluginMap.id] = pluginMap; 1056 }, 1057 1058 enable: function () { 1059 enabledRegistry[this.map.id] = this; 1060 this.enabled = true; 1061 1062 //Set flag mentioning that the module is enabling, 1063 //so that immediate calls to the defined callbacks 1064 //for dependencies do not trigger inadvertent load 1065 //with the depCount still being zero. 1066 this.enabling = true; 1067 1068 //Enable each dependency 1069 each(this.depMaps, bind(this, function (depMap, i) { 1070 var id, mod, handler; 1071 1072 if (typeof depMap === 'string') { 1073 //Dependency needs to be converted to a depMap 1074 //and wired up to this module. 1075 depMap = makeModuleMap(depMap, 1076 (this.map.isDefine ? this.map : this.map.parentMap), 1077 false, 1078 !this.skipMap); 1079 this.depMaps[i] = depMap; 1080 1081 handler = getOwn(handlers, depMap.id); 1082 1083 if (handler) { 1084 this.depExports[i] = handler(this); 1085 return; 1086 } 1087 1088 this.depCount += 1; 1089 1090 on(depMap, 'defined', bind(this, function (depExports) { 1091 this.defineDep(i, depExports); 1092 this.check(); 1093 })); 1094 1095 if (this.errback) { 1096 on(depMap, 'error', this.errback); 1097 } 1098 } 1099 1100 id = depMap.id; 1101 mod = registry[id]; 1102 1103 //Skip special modules like 'require', 'exports', 'module' 1104 //Also, don't call enable if it is already enabled, 1105 //important in circular dependency cases. 1106 if (!hasProp(handlers, id) && mod && !mod.enabled) { 1107 context.enable(depMap, this); 1108 } 1109 })); 1110 1111 //Enable each plugin that is used in 1112 //a dependency 1113 eachProp(this.pluginMaps, bind(this, function (pluginMap) { 1114 var mod = getOwn(registry, pluginMap.id); 1115 if (mod && !mod.enabled) { 1116 context.enable(pluginMap, this); 1117 } 1118 })); 1119 1120 this.enabling = false; 1121 1122 this.check(); 1123 }, 1124 1125 on: function (name, cb) { 1126 var cbs = this.events[name]; 1127 if (!cbs) { 1128 cbs = this.events[name] = []; 1129 } 1130 cbs.push(cb); 1131 }, 1132 1133 emit: function (name, evt) { 1134 each(this.events[name], function (cb) { 1135 cb(evt); 1136 }); 1137 if (name === 'error') { 1138 //Now that the error handler was triggered, remove 1139 //the listeners, since this broken Module instance 1140 //can stay around for a while in the registry. 1141 delete this.events[name]; 1142 } 1143 } 1144 }; 1145 1146 function callGetModule(args) { 1147 //Skip modules already defined. 1148 if (!hasProp(defined, args[0])) { 1149 getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]); 1150 } 1151 } 1152 1153 function removeListener(node, func, name, ieName) { 1154 //Favor detachEvent because of IE9 1155 //issue, see attachEvent/addEventListener comment elsewhere 1156 //in this file. 1157 if (node.detachEvent && !isOpera) { 1158 //Probably IE. If not it will throw an error, which will be 1159 //useful to know. 1160 if (ieName) { 1161 node.detachEvent(ieName, func); 1162 } 1163 } else { 1164 node.removeEventListener(name, func, false); 1165 } 1166 } 1167 1168 /** 1169 * Given an event from a script node, get the requirejs info from it, 1170 * and then removes the event listeners on the node. 1171 * @param {Event} evt 1172 * @returns {Object} 1173 */ 1174 function getScriptData(evt) { 1175 //Using currentTarget instead of target for Firefox 2.0's sake. Not 1176 //all old browsers will be supported, but this one was easy enough 1177 //to support and still makes sense. 1178 var node = evt.currentTarget || evt.srcElement; 1179 1180 //Remove the listeners once here. 1181 removeListener(node, context.onScriptLoad, 'load', 'onreadystatechange'); 1182 removeListener(node, context.onScriptError, 'error'); 1183 1184 return { 1185 node: node, 1186 id: node && node.getAttribute('data-requiremodule') 1187 }; 1188 } 1189 1190 function intakeDefines() { 1191 var args; 1192 1193 //Any defined modules in the global queue, intake them now. 1194 takeGlobalQueue(); 1195 1196 //Make sure any remaining defQueue items get properly processed. 1197 while (defQueue.length) { 1198 args = defQueue.shift(); 1199 if (args[0] === null) { 1200 return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1])); 1201 } else { 1202 //args are id, deps, factory. Should be normalized by the 1203 //define() function. 1204 callGetModule(args); 1205 } 1206 } 1207 } 1208 1209 context = { 1210 config: config, 1211 contextName: contextName, 1212 registry: registry, 1213 defined: defined, 1214 urlFetched: urlFetched, 1215 defQueue: defQueue, 1216 Module: Module, 1217 makeModuleMap: makeModuleMap, 1218 nextTick: req.nextTick, 1219 onError: onError, 1220 1221 /** 1222 * Set a configuration for the context. 1223 * @param {Object} cfg config object to integrate. 1224 */ 1225 configure: function (cfg) { 1226 //Make sure the baseUrl ends in a slash. 1227 if (cfg.baseUrl) { 1228 if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== '/') { 1229 cfg.baseUrl += '/'; 1230 } 1231 } 1232 1233 //Save off the paths and packages since they require special processing, 1234 //they are additive. 1235 var pkgs = config.pkgs, 1236 shim = config.shim, 1237 objs = { 1238 paths: true, 1239 config: true, 1240 map: true 1241 }; 1242 1243 eachProp(cfg, function (value, prop) { 1244 if (objs[prop]) { 1245 if (prop === 'map') { 1246 if (!config.map) { 1247 config.map = {}; 1248 } 1249 mixin(config[prop], value, true, true); 1250 } else { 1251 mixin(config[prop], value, true); 1252 } 1253 } else { 1254 config[prop] = value; 1255 } 1256 }); 1257 1258 //Merge shim 1259 if (cfg.shim) { 1260 eachProp(cfg.shim, function (value, id) { 1261 //Normalize the structure 1262 if (isArray(value)) { 1263 value = { 1264 deps: value 1265 }; 1266 } 1267 if ((value.exports || value.init) && !value.exportsFn) { 1268 value.exportsFn = context.makeShimExports(value); 1269 } 1270 shim[id] = value; 1271 }); 1272 config.shim = shim; 1273 } 1274 1275 //Adjust packages if necessary. 1276 if (cfg.packages) { 1277 each(cfg.packages, function (pkgObj) { 1278 var location; 1279 1280 pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj; 1281 location = pkgObj.location; 1282 1283 //Create a brand new object on pkgs, since currentPackages can 1284 //be passed in again, and config.pkgs is the internal transformed 1285 //state for all package configs. 1286 pkgs[pkgObj.name] = { 1287 name: pkgObj.name, 1288 location: location || pkgObj.name, 1289 //Remove leading dot in main, so main paths are normalized, 1290 //and remove any trailing .js, since different package 1291 //envs have different conventions: some use a module name, 1292 //some use a file name. 1293 main: (pkgObj.main || 'main') 1294 .replace(currDirRegExp, '') 1295 .replace(jsSuffixRegExp, '') 1296 }; 1297 }); 1298 1299 //Done with modifications, assing packages back to context config 1300 config.pkgs = pkgs; 1301 } 1302 1303 //If there are any "waiting to execute" modules in the registry, 1304 //update the maps for them, since their info, like URLs to load, 1305 //may have changed. 1306 eachProp(registry, function (mod, id) { 1307 //If module already has init called, since it is too 1308 //late to modify them, and ignore unnormalized ones 1309 //since they are transient. 1310 if (!mod.inited && !mod.map.unnormalized) { 1311 mod.map = makeModuleMap(id); 1312 } 1313 }); 1314 1315 //If a deps array or a config callback is specified, then call 1316 //require with those args. This is useful when require is defined as a 1317 //config object before require.js is loaded. 1318 if (cfg.deps || cfg.callback) { 1319 context.require(cfg.deps || [], cfg.callback); 1320 } 1321 }, 1322 1323 makeShimExports: function (value) { 1324 function fn() { 1325 var ret; 1326 if (value.init) { 1327 ret = value.init.apply(global, arguments); 1328 } 1329 return ret || (value.exports && getGlobal(value.exports)); 1330 } 1331 return fn; 1332 }, 1333 1334 makeRequire: function (relMap, options) { 1335 options = options || {}; 1336 1337 function localRequire(deps, callback, errback) { 1338 var id, map, requireMod; 1339 1340 if (options.enableBuildCallback && callback && isFunction(callback)) { 1341 callback.__requireJsBuild = true; 1342 } 1343 1344 if (typeof deps === 'string') { 1345 if (isFunction(callback)) { 1346 //Invalid call 1347 return onError(makeError('requireargs', 'Invalid require call'), errback); 1348 } 1349 1350 //If require|exports|module are requested, get the 1351 //value for them from the special handlers. Caveat: 1352 //this only works while module is being defined. 1353 if (relMap && hasProp(handlers, deps)) { 1354 return handlers[deps](registry[relMap.id]); 1355 } 1356 1357 //Synchronous access to one module. If require.get is 1358 //available (as in the Node adapter), prefer that. 1359 if (req.get) { 1360 return req.get(context, deps, relMap, localRequire); 1361 } 1362 1363 //Normalize module name, if it contains . or .. 1364 map = makeModuleMap(deps, relMap, false, true); 1365 id = map.id; 1366 1367 if (!hasProp(defined, id)) { 1368 return onError(makeError('notloaded', 'Module name "' + 1369 id + 1370 '" has not been loaded yet for context: ' + 1371 contextName + 1372 (relMap ? '' : '. Use require([])'))); 1373 } 1374 return defined[id]; 1375 } 1376 1377 //Grab defines waiting in the global queue. 1378 intakeDefines(); 1379 1380 //Mark all the dependencies as needing to be loaded. 1381 context.nextTick(function () { 1382 //Some defines could have been added since the 1383 //require call, collect them. 1384 intakeDefines(); 1385 1386 requireMod = getModule(makeModuleMap(null, relMap)); 1387 1388 //Store if map config should be applied to this require 1389 //call for dependencies. 1390 requireMod.skipMap = options.skipMap; 1391 1392 requireMod.init(deps, callback, errback, { 1393 enabled: true 1394 }); 1395 1396 checkLoaded(); 1397 }); 1398 1399 return localRequire; 1400 } 1401 1402 mixin(localRequire, { 1403 isBrowser: isBrowser, 1404 1405 /** 1406 * Converts a module name + .extension into an URL path. 1407 * *Requires* the use of a module name. It does not support using 1408 * plain URLs like nameToUrl. 1409 */ 1410 toUrl: function (moduleNamePlusExt) { 1411 var ext, 1412 index = moduleNamePlusExt.lastIndexOf('.'), 1413 segment = moduleNamePlusExt.split('/')[0], 1414 isRelative = segment === '.' || segment === '..'; 1415 1416 //Have a file extension alias, and it is not the 1417 //dots from a relative path. 1418 if (index !== -1 && (!isRelative || index > 1)) { 1419 ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length); 1420 moduleNamePlusExt = moduleNamePlusExt.substring(0, index); 1421 } 1422 1423 return context.nameToUrl(normalize(moduleNamePlusExt, 1424 relMap && relMap.id, true), ext, true); 1425 }, 1426 1427 defined: function (id) { 1428 return hasProp(defined, makeModuleMap(id, relMap, false, true).id); 1429 }, 1430 1431 specified: function (id) { 1432 id = makeModuleMap(id, relMap, false, true).id; 1433 return hasProp(defined, id) || hasProp(registry, id); 1434 } 1435 }); 1436 1437 //Only allow undef on top level require calls 1438 if (!relMap) { 1439 localRequire.undef = function (id) { 1440 //Bind any waiting define() calls to this context, 1441 //fix for #408 1442 takeGlobalQueue(); 1443 1444 var map = makeModuleMap(id, relMap, true), 1445 mod = getOwn(registry, id); 1446 1447 delete defined[id]; 1448 delete urlFetched[map.url]; 1449 delete undefEvents[id]; 1450 1451 if (mod) { 1452 //Hold on to listeners in case the 1453 //module will be attempted to be reloaded 1454 //using a different config. 1455 if (mod.events.defined) { 1456 undefEvents[id] = mod.events; 1457 } 1458 1459 cleanRegistry(id); 1460 } 1461 }; 1462 } 1463 1464 return localRequire; 1465 }, 1466 1467 /** 1468 * Called to enable a module if it is still in the registry 1469 * awaiting enablement. A second arg, parent, the parent module, 1470 * is passed in for context, when this method is overriden by 1471 * the optimizer. Not shown here to keep code compact. 1472 */ 1473 enable: function (depMap) { 1474 var mod = getOwn(registry, depMap.id); 1475 if (mod) { 1476 getModule(depMap).enable(); 1477 } 1478 }, 1479 1480 /** 1481 * Internal method used by environment adapters to complete a load event. 1482 * A load event could be a script load or just a load pass from a synchronous 1483 * load call. 1484 * @param {String} moduleName the name of the module to potentially complete. 1485 */ 1486 completeLoad: function (moduleName) { 1487 var found, args, mod, 1488 shim = getOwn(config.shim, moduleName) || {}, 1489 shExports = shim.exports; 1490 1491 takeGlobalQueue(); 1492 1493 while (defQueue.length) { 1494 args = defQueue.shift(); 1495 if (args[0] === null) { 1496 args[0] = moduleName; 1497 //If already found an anonymous module and bound it 1498 //to this name, then this is some other anon module 1499 //waiting for its completeLoad to fire. 1500 if (found) { 1501 break; 1502 } 1503 found = true; 1504 } else if (args[0] === moduleName) { 1505 //Found matching define call for this script! 1506 found = true; 1507 } 1508 1509 callGetModule(args); 1510 } 1511 1512 //Do this after the cycle of callGetModule in case the result 1513 //of those calls/init calls changes the registry. 1514 mod = getOwn(registry, moduleName); 1515 1516 if (!found && !hasProp(defined, moduleName) && mod && !mod.inited) { 1517 if (config.enforceDefine && (!shExports || !getGlobal(shExports))) { 1518 if (hasPathFallback(moduleName)) { 1519 return; 1520 } else { 1521 return onError(makeError('nodefine', 1522 'No define call for ' + moduleName, 1523 null, 1524 [moduleName])); 1525 } 1526 } else { 1527 //A script that does not call define(), so just simulate 1528 //the call for it. 1529 callGetModule([moduleName, (shim.deps || []), shim.exportsFn]); 1530 } 1531 } 1532 1533 checkLoaded(); 1534 }, 1535 1536 /** 1537 * Converts a module name to a file path. Supports cases where 1538 * moduleName may actually be just an URL. 1539 * Note that it **does not** call normalize on the moduleName, 1540 * it is assumed to have already been normalized. This is an 1541 * internal API, not a public one. Use toUrl for the public API. 1542 */ 1543 nameToUrl: function (moduleName, ext, skipExt) { 1544 var paths, pkgs, pkg, pkgPath, syms, i, parentModule, url, 1545 parentPath; 1546 1547 //If a colon is in the URL, it indicates a protocol is used and it is just 1548 //an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?) 1549 //or ends with .js, then assume the user meant to use an url and not a module id. 1550 //The slash is important for protocol-less URLs as well as full paths. 1551 if (req.jsExtRegExp.test(moduleName)) { 1552 //Just a plain path, not module name lookup, so just return it. 1553 //Add extension if it is included. This is a bit wonky, only non-.js things pass 1554 //an extension, this method probably needs to be reworked. 1555 url = moduleName + (ext || ''); 1556 } else { 1557 //A module that needs to be converted to a path. 1558 paths = config.paths; 1559 pkgs = config.pkgs; 1560 1561 syms = moduleName.split('/'); 1562 //For each module name segment, see if there is a path 1563 //registered for it. Start with most specific name 1564 //and work up from it. 1565 for (i = syms.length; i > 0; i -= 1) { 1566 parentModule = syms.slice(0, i).join('/'); 1567 pkg = getOwn(pkgs, parentModule); 1568 parentPath = getOwn(paths, parentModule); 1569 if (parentPath) { 1570 //If an array, it means there are a few choices, 1571 //Choose the one that is desired 1572 if (isArray(parentPath)) { 1573 parentPath = parentPath[0]; 1574 } 1575 syms.splice(0, i, parentPath); 1576 break; 1577 } else if (pkg) { 1578 //If module name is just the package name, then looking 1579 //for the main module. 1580 if (moduleName === pkg.name) { 1581 pkgPath = pkg.location + '/' + pkg.main; 1582 } else { 1583 pkgPath = pkg.location; 1584 } 1585 syms.splice(0, i, pkgPath); 1586 break; 1587 } 1588 } 1589 1590 //Join the path parts together, then figure out if baseUrl is needed. 1591 url = syms.join('/'); 1592 url += (ext || (/\?/.test(url) || skipExt ? '' : '.js')); 1593 url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url; 1594 } 1595 1596 return config.urlArgs ? url + 1597 ((url.indexOf('?') === -1 ? '?' : '&') + 1598 config.urlArgs) : url; 1599 }, 1600 1601 //Delegates to req.load. Broken out as a separate function to 1602 //allow overriding in the optimizer. 1603 load: function (id, url) { 1604 req.load(context, id, url); 1605 }, 1606 1607 /** 1608 * Executes a module callack function. Broken out as a separate function 1609 * solely to allow the build system to sequence the files in the built 1610 * layer in the right sequence. 1611 * 1612 * @private 1613 */ 1614 execCb: function (name, callback, args, exports) { 1615 return callback.apply(exports, args); 1616 }, 1617 1618 /** 1619 * callback for script loads, used to check status of loading. 1620 * 1621 * @param {Event} evt the event from the browser for the script 1622 * that was loaded. 1623 */ 1624 onScriptLoad: function (evt) { 1625 //Using currentTarget instead of target for Firefox 2.0's sake. Not 1626 //all old browsers will be supported, but this one was easy enough 1627 //to support and still makes sense. 1628 if (evt.type === 'load' || 1629 (readyRegExp.test((evt.currentTarget || evt.srcElement).readyState))) { 1630 //Reset interactive script so a script node is not held onto for 1631 //to long. 1632 interactiveScript = null; 1633 1634 //Pull out the name of the module and the context. 1635 var data = getScriptData(evt); 1636 context.completeLoad(data.id); 1637 } 1638 }, 1639 1640 /** 1641 * Callback for script errors. 1642 */ 1643 onScriptError: function (evt) { 1644 var data = getScriptData(evt); 1645 if (!hasPathFallback(data.id)) { 1646 return onError(makeError('scripterror', 'Script error', evt, [data.id])); 1647 } 1648 } 1649 }; 1650 1651 context.require = context.makeRequire(); 1652 return context; 1653 } 1654 1655 /** 1656 * Main entry point. 1657 * 1658 * If the only argument to require is a string, then the module that 1659 * is represented by that string is fetched for the appropriate context. 1660 * 1661 * If the first argument is an array, then it will be treated as an array 1662 * of dependency string names to fetch. An optional function callback can 1663 * be specified to execute when all of those dependencies are available. 1664 * 1665 * Make a local req variable to help Caja compliance (it assumes things 1666 * on a require that are not standardized), and to give a short 1667 * name for minification/local scope use. 1668 */ 1669 req = requirejs = function (deps, callback, errback, optional) { 1670 1671 //Find the right context, use default 1672 var context, config, 1673 contextName = defContextName; 1674 1675 // Determine if have config object in the call. 1676 if (!isArray(deps) && typeof deps !== 'string') { 1677 // deps is a config object 1678 config = deps; 1679 if (isArray(callback)) { 1680 // Adjust args if there are dependencies 1681 deps = callback; 1682 callback = errback; 1683 errback = optional; 1684 } else { 1685 deps = []; 1686 } 1687 } 1688 1689 if (config && config.context) { 1690 contextName = config.context; 1691 } 1692 1693 context = getOwn(contexts, contextName); 1694 if (!context) { 1695 context = contexts[contextName] = req.s.newContext(contextName); 1696 } 1697 1698 if (config) { 1699 context.configure(config); 1700 } 1701 1702 return context.require(deps, callback, errback); 1703 }; 1704 1705 /** 1706 * Support require.config() to make it easier to cooperate with other 1707 * AMD loaders on globally agreed names. 1708 */ 1709 req.config = function (config) { 1710 return req(config); 1711 }; 1712 1713 /** 1714 * Execute something after the current tick 1715 * of the event loop. Override for other envs 1716 * that have a better solution than setTimeout. 1717 * @param {Function} fn function to execute later. 1718 */ 1719 req.nextTick = typeof setTimeout !== 'undefined' ? function (fn) { 1720 setTimeout(fn, 4); 1721 } : function (fn) { fn(); }; 1722 1723 /** 1724 * Export require as a global, but only if it does not already exist. 1725 */ 1726 if (!require) { 1727 require = req; 1728 } 1729 1730 req.version = version; 1731 1732 //Used to filter out dependencies that are already paths. 1733 req.jsExtRegExp = /^\/|:|\?|\.js$/; 1734 req.isBrowser = isBrowser; 1735 s = req.s = { 1736 contexts: contexts, 1737 newContext: newContext 1738 }; 1739 1740 //Create default context. 1741 req({}); 1742 1743 //Exports some context-sensitive methods on global require. 1744 each([ 1745 'toUrl', 1746 'undef', 1747 'defined', 1748 'specified' 1749 ], function (prop) { 1750 //Reference from contexts instead of early binding to default context, 1751 //so that during builds, the latest instance of the default context 1752 //with its config gets used. 1753 req[prop] = function () { 1754 var ctx = contexts[defContextName]; 1755 return ctx.require[prop].apply(ctx, arguments); 1756 }; 1757 }); 1758 1759 if (isBrowser) { 1760 head = s.head = document.getElementsByTagName('head')[0]; 1761 //If BASE tag is in play, using appendChild is a problem for IE6. 1762 //When that browser dies, this can be removed. Details in this jQuery bug: 1763 //http://dev.jquery.com/ticket/2709 1764 baseElement = document.getElementsByTagName('base')[0]; 1765 if (baseElement) { 1766 head = s.head = baseElement.parentNode; 1767 } 1768 } 1769 1770 /** 1771 * Any errors that require explicitly generates will be passed to this 1772 * function. Intercept/override it if you want custom error handling. 1773 * @param {Error} err the error object. 1774 */ 1775 req.onError = function (err) { 1776 throw err; 1777 }; 1778 1779 /** 1780 * Does the request to load a module for the browser case. 1781 * Make this a separate function to allow other environments 1782 * to override it. 1783 * 1784 * @param {Object} context the require context to find state. 1785 * @param {String} moduleName the name of the module. 1786 * @param {Object} url the URL to the module. 1787 */ 1788 req.load = function (context, moduleName, url) { 1789 var config = (context && context.config) || {}, 1790 node; 1791 if (isBrowser) { 1792 //In the browser so use a script tag 1793 node = config.xhtml ? 1794 document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') : 1795 document.createElement('script'); 1796 node.type = config.scriptType || 'text/javascript'; 1797 node.charset = 'utf-8'; 1798 node.async = true; 1799 1800 node.setAttribute('data-requirecontext', context.contextName); 1801 node.setAttribute('data-requiremodule', moduleName); 1802 1803 //Set up load listener. Test attachEvent first because IE9 has 1804 //a subtle issue in its addEventListener and script onload firings 1805 //that do not match the behavior of all other browsers with 1806 //addEventListener support, which fire the onload event for a 1807 //script right after the script execution. See: 1808 //https://connect.microsoft.com/IE/feedback/details/648057/script-onload-event-is-not-fired-immediately-after-script-execution 1809 //UNFORTUNATELY Opera implements attachEvent but does not follow the script 1810 //script execution mode. 1811 if (node.attachEvent && 1812 //Check if node.attachEvent is artificially added by custom script or 1813 //natively supported by browser 1814 //read https://github.com/jrburke/requirejs/issues/187 1815 //if we can NOT find [native code] then it must NOT natively supported. 1816 //in IE8, node.attachEvent does not have toString() 1817 //Note the test for "[native code" with no closing brace, see: 1818 //https://github.com/jrburke/requirejs/issues/273 1819 !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) && 1820 !isOpera) { 1821 //Probably IE. IE (at least 6-8) do not fire 1822 //script onload right after executing the script, so 1823 //we cannot tie the anonymous define call to a name. 1824 //However, IE reports the script as being in 'interactive' 1825 //readyState at the time of the define call. 1826 useInteractive = true; 1827 1828 node.attachEvent('onreadystatechange', context.onScriptLoad); 1829 //It would be great to add an error handler here to catch 1830 //404s in IE9+. However, onreadystatechange will fire before 1831 //the error handler, so that does not help. If addEventListener 1832 //is used, then IE will fire error before load, but we cannot 1833 //use that pathway given the connect.microsoft.com issue 1834 //mentioned above about not doing the 'script execute, 1835 //then fire the script load event listener before execute 1836 //next script' that other browsers do. 1837 //Best hope: IE10 fixes the issues, 1838 //and then destroys all installs of IE 6-9. 1839 //node.attachEvent('onerror', context.onScriptError); 1840 } else { 1841 node.addEventListener('load', context.onScriptLoad, false); 1842 node.addEventListener('error', context.onScriptError, false); 1843 } 1844 node.src = url; 1845 1846 //For some cache cases in IE 6-8, the script executes before the end 1847 //of the appendChild execution, so to tie an anonymous define 1848 //call to the module name (which is stored on the node), hold on 1849 //to a reference to this node, but clear after the DOM insertion. 1850 currentlyAddingScript = node; 1851 if (baseElement) { 1852 head.insertBefore(node, baseElement); 1853 } else { 1854 head.appendChild(node); 1855 } 1856 currentlyAddingScript = null; 1857 1858 return node; 1859 } else if (isWebWorker) { 1860 try { 1861 //In a web worker, use importScripts. This is not a very 1862 //efficient use of importScripts, importScripts will block until 1863 //its script is downloaded and evaluated. However, if web workers 1864 //are in play, the expectation that a build has been done so that 1865 //only one script needs to be loaded anyway. This may need to be 1866 //reevaluated if other use cases become common. 1867 importScripts(url); 1868 1869 //Account for anonymous modules 1870 context.completeLoad(moduleName); 1871 } catch (e) { 1872 context.onError(makeError('importscripts', 1873 'importScripts failed for ' + 1874 moduleName + ' at ' + url, 1875 e, 1876 [moduleName])); 1877 } 1878 } 1879 }; 1880 1881 function getInteractiveScript() { 1882 if (interactiveScript && interactiveScript.readyState === 'interactive') { 1883 return interactiveScript; 1884 } 1885 1886 eachReverse(scripts(), function (script) { 1887 if (script.readyState === 'interactive') { 1888 return (interactiveScript = script); 1889 } 1890 }); 1891 return interactiveScript; 1892 } 1893 1894 //Look for a data-main script attribute, which could also adjust the baseUrl. 1895 if (isBrowser) { 1896 //Figure out baseUrl. Get it from the script tag with require.js in it. 1897 eachReverse(scripts(), function (script) { 1898 //Set the 'head' where we can append children by 1899 //using the script's parent. 1900 if (!head) { 1901 head = script.parentNode; 1902 } 1903 1904 //Look for a data-main attribute to set main script for the page 1905 //to load. If it is there, the path to data main becomes the 1906 //baseUrl, if it is not already set. 1907 dataMain = script.getAttribute('data-main'); 1908 if (dataMain) { 1909 //Set final baseUrl if there is not already an explicit one. 1910 if (!cfg.baseUrl) { 1911 //Pull off the directory of data-main for use as the 1912 //baseUrl. 1913 src = dataMain.split('/'); 1914 mainScript = src.pop(); 1915 subPath = src.length ? src.join('/') + '/' : './'; 1916 1917 cfg.baseUrl = subPath; 1918 dataMain = mainScript; 1919 } 1920 1921 //Strip off any trailing .js since dataMain is now 1922 //like a module name. 1923 dataMain = dataMain.replace(jsSuffixRegExp, ''); 1924 1925 //Put the data-main script in the files to load. 1926 cfg.deps = cfg.deps ? cfg.deps.concat(dataMain) : [dataMain]; 1927 1928 return true; 1929 } 1930 }); 1931 } 1932 1933 /** 1934 * The function that handles definitions of modules. Differs from 1935 * require() in that a string for the module should be the first argument, 1936 * and the function to execute after dependencies are loaded should 1937 * return a value to define the module corresponding to the first argument's 1938 * name. 1939 */ 1940 define = function (name, deps, callback) { 1941 var node, context; 1942 1943 //Allow for anonymous modules 1944 if (typeof name !== 'string') { 1945 //Adjust args appropriately 1946 callback = deps; 1947 deps = name; 1948 name = null; 1949 } 1950 1951 //This module may not have dependencies 1952 if (!isArray(deps)) { 1953 callback = deps; 1954 deps = []; 1955 } 1956 1957 //If no name, and callback is a function, then figure out if it a 1958 //CommonJS thing with dependencies. 1959 if (!deps.length && isFunction(callback)) { 1960 //Remove comments from the callback string, 1961 //look for require calls, and pull them into the dependencies, 1962 //but only if there are function args. 1963 if (callback.length) { 1964 callback 1965 .toString() 1966 .replace(commentRegExp, '') 1967 .replace(cjsRequireRegExp, function (match, dep) { 1968 deps.push(dep); 1969 }); 1970 1971 //May be a CommonJS thing even without require calls, but still 1972 //could use exports, and module. Avoid doing exports and module 1973 //work though if it just needs require. 1974 //REQUIRES the function to expect the CommonJS variables in the 1975 //order listed below. 1976 deps = (callback.length === 1 ? ['require'] : ['require', 'exports', 'module']).concat(deps); 1977 } 1978 } 1979 1980 //If in IE 6-8 and hit an anonymous define() call, do the interactive 1981 //work. 1982 if (useInteractive) { 1983 node = currentlyAddingScript || getInteractiveScript(); 1984 if (node) { 1985 if (!name) { 1986 name = node.getAttribute('data-requiremodule'); 1987 } 1988 context = contexts[node.getAttribute('data-requirecontext')]; 1989 } 1990 } 1991 1992 //Always save off evaluating the def call until the script onload handler. 1993 //This allows multiple modules to be in a file without prematurely 1994 //tracing dependencies, and allows for anonymous module support, 1995 //where the module name is not known until the script onload event 1996 //occurs. If no context, use the global queue, and get it processed 1997 //in the onscript load callback. 1998 (context ? context.defQueue : globalDefQueue).push([name, deps, callback]); 1999 }; 2000 2001 define.amd = { 2002 jQuery: true 2003 }; 2004 2005 2006 /** 2007 * Executes the text. Normally just uses eval, but can be modified 2008 * to use a better, environment-specific call. Only used for transpiling 2009 * loader plugins, not for plain JS modules. 2010 * @param {String} text the text to execute/evaluate. 2011 */ 2012 req.exec = function (text) { 2013 /*jslint evil: true */ 2014 return eval(text); 2015 }; 2016 2017 //Set up with config info. 2018 req(cfg); 2019}(this)); 2020 2021var components = { 2022 "packages": [ 2023 { 2024 "name": "jplayer", 2025 "main": "jplayer-built.js" 2026 } 2027 ], 2028 "shim": { 2029 "jplayer": { 2030 "deps": [ 2031 "jquery" 2032 ] 2033 } 2034 }, 2035 "baseUrl": "components" 2036}; 2037if (typeof require !== "undefined" && require.config) { 2038 require.config(components); 2039} else { 2040 var require = components; 2041} 2042if (typeof exports !== "undefined" && typeof module !== "undefined") { 2043 module.exports = components; 2044} 2045define('jplayer', function (require, exports, module) { 2046/* 2047 * jPlayer Plugin for jQuery JavaScript Library 2048 * http://www.jplayer.org 2049 * 2050 * Copyright (c) 2009 - 2014 Happyworm Ltd 2051 * Licensed under the MIT license. 2052 * http://opensource.org/licenses/MIT 2053 * 2054 * Author: Mark J Panaghiston 2055 * Version: 2.9.2 2056 * Date: 14th December 2014 2057 */ 2058 2059/* Support for Zepto 1.0 compiled with optional data module. 2060 * For AMD or NODE/CommonJS support, you will need to manually switch the related 2 lines in the code below. 2061 * Search terms: "jQuery Switch" and "Zepto Switch" 2062 */ 2063 2064(function (root, factory) { 2065 if (typeof define === 'function' && define.amd) { 2066 // AMD. Register as an anonymous module. 2067 define(['jquery'], factory); // jQuery Switch 2068 // define(['zepto'], factory); // Zepto Switch 2069 } else if (typeof exports === 'object') { 2070 // Node/CommonJS 2071 factory(require('jquery')); // jQuery Switch 2072 //factory(require('zepto')); // Zepto Switch 2073 } else { 2074 // Browser globals 2075 if(root.jQuery) { // Use jQuery if available 2076 factory(root.jQuery); 2077 } else { // Otherwise, use Zepto 2078 factory(root.Zepto); 2079 } 2080 } 2081}(this, function ($, undefined) { 2082 2083 // Adapted from jquery.ui.widget.js (1.8.7): $.widget.bridge - Tweaked $.data(this,XYZ) to $(this).data(XYZ) for Zepto 2084 $.fn.jPlayer = function( options ) { 2085 var name = "jPlayer"; 2086 var isMethodCall = typeof options === "string", 2087 args = Array.prototype.slice.call( arguments, 1 ), 2088 returnValue = this; 2089 2090 // allow multiple hashes to be passed on init 2091 options = !isMethodCall && args.length ? 2092 $.extend.apply( null, [ true, options ].concat(args) ) : 2093 options; 2094 2095 // prevent calls to internal methods 2096 if ( isMethodCall && options.charAt( 0 ) === "_" ) { 2097 return returnValue; 2098 } 2099 2100 if ( isMethodCall ) { 2101 this.each(function() { 2102 var instance = $(this).data( name ), 2103 methodValue = instance && $.isFunction( instance[options] ) ? 2104 instance[ options ].apply( instance, args ) : 2105 instance; 2106 if ( methodValue !== instance && methodValue !== undefined ) { 2107 returnValue = methodValue; 2108 return false; 2109 } 2110 }); 2111 } else { 2112 this.each(function() { 2113 var instance = $(this).data( name ); 2114 if ( instance ) { 2115 // instance.option( options || {} )._init(); // Orig jquery.ui.widget.js code: Not recommend for jPlayer. ie., Applying new options to an existing instance (via the jPlayer constructor) and performing the _init(). The _init() is what concerns me. It would leave a lot of event handlers acting on jPlayer instance and the interface. 2116 instance.option( options || {} ); // The new constructor only changes the options. Changing options only has basic support atm. 2117 } else { 2118 $(this).data( name, new $.jPlayer( options, this ) ); 2119 } 2120 }); 2121 } 2122 2123 return returnValue; 2124 }; 2125 2126 $.jPlayer = function( options, element ) { 2127 // allow instantiation without initializing for simple inheritance 2128 if ( arguments.length ) { 2129 this.element = $(element); 2130 this.options = $.extend(true, {}, 2131 this.options, 2132 options 2133 ); 2134 var self = this; 2135 this.element.bind( "remove.jPlayer", function() { 2136 self.destroy(); 2137 }); 2138 this._init(); 2139 } 2140 }; 2141 // End of: (Adapted from jquery.ui.widget.js (1.8.7)) 2142 2143 // Zepto is missing one of the animation methods. 2144 if(typeof $.fn.stop !== 'function') { 2145 $.fn.stop = function() {}; 2146 } 2147 2148 // Emulated HTML5 methods and properties 2149 $.jPlayer.emulateMethods = "load play pause"; 2150 $.jPlayer.emulateStatus = "src readyState networkState currentTime duration paused ended playbackRate"; 2151 $.jPlayer.emulateOptions = "muted volume"; 2152 2153 // Reserved event names generated by jPlayer that are not part of the HTML5 Media element spec 2154 $.jPlayer.reservedEvent = "ready flashreset resize repeat error warning"; 2155 2156 // Events generated by jPlayer 2157 $.jPlayer.event = {}; 2158 $.each( 2159 [ 2160 'ready', 2161 'setmedia', // Fires when the media is set 2162 'flashreset', // Similar to the ready event if the Flash solution is set to display:none and then shown again or if it's reloaded for another reason by the browser. For example, using CSS position:fixed on Firefox for the full screen feature. 2163 'resize', // Occurs when the size changes through a full/restore screen operation or if the size/sizeFull options are changed. 2164 'repeat', // Occurs when the repeat status changes. Usually through clicks on the repeat button of the interface. 2165 'click', // Occurs when the user clicks on one of the following: poster image, html video, flash video. 2166 'error', // Event error code in event.jPlayer.error.type. See $.jPlayer.error 2167 'warning', // Event warning code in event.jPlayer.warning.type. See $.jPlayer.warning 2168 2169 // Other events match HTML5 spec. 2170 'loadstart', 2171 'progress', 2172 'suspend', 2173 'abort', 2174 'emptied', 2175 'stalled', 2176 'play', 2177 'pause', 2178 'loadedmetadata', 2179 'loadeddata', 2180 'waiting', 2181 'playing', 2182 'canplay', 2183 'canplaythrough', 2184 'seeking', 2185 'seeked', 2186 'timeupdate', 2187 'ended', 2188 'ratechange', 2189 'durationchange', 2190 'volumechange' 2191 ], 2192 function() { 2193 $.jPlayer.event[ this ] = 'jPlayer_' + this; 2194 } 2195 ); 2196 2197 $.jPlayer.htmlEvent = [ // These HTML events are bubbled through to the jPlayer event, without any internal action. 2198 "loadstart", 2199 // "progress", // jPlayer uses internally before bubbling. 2200 // "suspend", // jPlayer uses internally before bubbling. 2201 "abort", 2202 // "error", // jPlayer uses internally before bubbling. 2203 "emptied", 2204 "stalled", 2205 // "play", // jPlayer uses internally before bubbling. 2206 // "pause", // jPlayer uses internally before bubbling. 2207 "loadedmetadata", 2208 // "loadeddata", // jPlayer uses internally before bubbling. 2209 // "waiting", // jPlayer uses internally before bubbling. 2210 // "playing", // jPlayer uses internally before bubbling. 2211 "canplay", 2212 "canplaythrough" 2213 // "seeking", // jPlayer uses internally before bubbling. 2214 // "seeked", // jPlayer uses internally before bubbling. 2215 // "timeupdate", // jPlayer uses internally before bubbling. 2216 // "ended", // jPlayer uses internally before bubbling. 2217 // "ratechange" // jPlayer uses internally before bubbling. 2218 // "durationchange" // jPlayer uses internally before bubbling. 2219 // "volumechange" // jPlayer uses internally before bubbling. 2220 ]; 2221 2222 $.jPlayer.pause = function() { 2223 $.jPlayer.prototype.destroyRemoved(); 2224 $.each($.jPlayer.prototype.instances, function(i, element) { 2225 if(element.data("jPlayer").status.srcSet) { // Check that media is set otherwise would cause error event. 2226 element.jPlayer("pause"); 2227 } 2228 }); 2229 }; 2230 2231 // Default for jPlayer option.timeFormat 2232 $.jPlayer.timeFormat = { 2233 showHour: false, 2234 showMin: true, 2235 showSec: true, 2236 padHour: false, 2237 padMin: true, 2238 padSec: true, 2239 sepHour: ":", 2240 sepMin: ":", 2241 sepSec: "" 2242 }; 2243 var ConvertTime = function() { 2244 this.init(); 2245 }; 2246 ConvertTime.prototype = { 2247 init: function() { 2248 this.options = { 2249 timeFormat: $.jPlayer.timeFormat 2250 }; 2251 }, 2252 time: function(s) { // function used on jPlayer.prototype._convertTime to enable per instance options. 2253 s = (s && typeof s === 'number') ? s : 0; 2254 2255 var myTime = new Date(s * 1000), 2256 hour = myTime.getUTCHours(), 2257 min = this.options.timeFormat.showHour ? myTime.getUTCMinutes() : myTime.getUTCMinutes() + hour * 60, 2258 sec = this.options.timeFormat.showMin ? myTime.getUTCSeconds() : myTime.getUTCSeconds() + min * 60, 2259 strHour = (this.options.timeFormat.padHour && hour < 10) ? "0" + hour : hour, 2260 strMin = (this.options.timeFormat.padMin && min < 10) ? "0" + min : min, 2261 strSec = (this.options.timeFormat.padSec && sec < 10) ? "0" + sec : sec, 2262 strTime = ""; 2263 2264 strTime += this.options.timeFormat.showHour ? strHour + this.options.timeFormat.sepHour : ""; 2265 strTime += this.options.timeFormat.showMin ? strMin + this.options.timeFormat.sepMin : ""; 2266 strTime += this.options.timeFormat.showSec ? strSec + this.options.timeFormat.sepSec : ""; 2267 2268 return strTime; 2269 } 2270 }; 2271 var myConvertTime = new ConvertTime(); 2272 $.jPlayer.convertTime = function(s) { 2273 return myConvertTime.time(s); 2274 }; 2275 2276 // Adapting jQuery 1.4.4 code for jQuery.browser. Required since jQuery 1.3.2 does not detect Chrome as webkit. 2277 $.jPlayer.uaBrowser = function( userAgent ) { 2278 var ua = userAgent.toLowerCase(); 2279 2280 // Useragent RegExp 2281 var rwebkit = /(webkit)[ \/]([\w.]+)/; 2282 var ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/; 2283 var rmsie = /(msie) ([\w.]+)/; 2284 var rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/; 2285 2286 var match = rwebkit.exec( ua ) || 2287 ropera.exec( ua ) || 2288 rmsie.exec( ua ) || 2289 ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) || 2290 []; 2291 2292 return { browser: match[1] || "", version: match[2] || "0" }; 2293 }; 2294 2295 // Platform sniffer for detecting mobile devices 2296 $.jPlayer.uaPlatform = function( userAgent ) { 2297 var ua = userAgent.toLowerCase(); 2298 2299 // Useragent RegExp 2300 var rplatform = /(ipad|iphone|ipod|android|blackberry|playbook|windows ce|webos)/; 2301 var rtablet = /(ipad|playbook)/; 2302 var randroid = /(android)/; 2303 var rmobile = /(mobile)/; 2304 2305 var platform = rplatform.exec( ua ) || []; 2306 var tablet = rtablet.exec( ua ) || 2307 !rmobile.exec( ua ) && randroid.exec( ua ) || 2308 []; 2309 2310 if(platform[1]) { 2311 platform[1] = platform[1].replace(/\s/g, "_"); // Change whitespace to underscore. Enables dot notation. 2312 } 2313 2314 return { platform: platform[1] || "", tablet: tablet[1] || "" }; 2315 }; 2316 2317 $.jPlayer.browser = { 2318 }; 2319 $.jPlayer.platform = { 2320 }; 2321 2322 var browserMatch = $.jPlayer.uaBrowser(navigator.userAgent); 2323 if ( browserMatch.browser ) { 2324 $.jPlayer.browser[ browserMatch.browser ] = true; 2325 $.jPlayer.browser.version = browserMatch.version; 2326 } 2327 var platformMatch = $.jPlayer.uaPlatform(navigator.userAgent); 2328 if ( platformMatch.platform ) { 2329 $.jPlayer.platform[ platformMatch.platform ] = true; 2330 $.jPlayer.platform.mobile = !platformMatch.tablet; 2331 $.jPlayer.platform.tablet = !!platformMatch.tablet; 2332 } 2333 2334 // Internet Explorer (IE) Browser Document Mode Sniffer. Based on code at: 2335 // http://msdn.microsoft.com/en-us/library/cc288325%28v=vs.85%29.aspx#GetMode 2336 $.jPlayer.getDocMode = function() { 2337 var docMode; 2338 if ($.jPlayer.browser.msie) { 2339 if (document.documentMode) { // IE8 or later 2340 docMode = document.documentMode; 2341 } else { // IE 5-7 2342 docMode = 5; // Assume quirks mode unless proven otherwise 2343 if (document.compatMode) { 2344 if (document.compatMode === "CSS1Compat") { 2345 docMode = 7; // standards mode 2346 } 2347 } 2348 } 2349 } 2350 return docMode; 2351 }; 2352 $.jPlayer.browser.documentMode = $.jPlayer.getDocMode(); 2353 2354 $.jPlayer.nativeFeatures = { 2355 init: function() { 2356 2357 /* Fullscreen function naming influenced by W3C naming. 2358 * No support for: Mozilla Proposal: https://wiki.mozilla.org/Gecko:FullScreenAPI 2359 */ 2360 2361 var d = document, 2362 v = d.createElement('video'), 2363 spec = { 2364 // http://www.w3.org/TR/fullscreen/ 2365 w3c: [ 2366 'fullscreenEnabled', 2367 'fullscreenElement', 2368 'requestFullscreen', 2369 'exitFullscreen', 2370 'fullscreenchange', 2371 'fullscreenerror' 2372 ], 2373 // https://developer.mozilla.org/en-US/docs/DOM/Using_fullscreen_mode 2374 moz: [ 2375 'mozFullScreenEnabled', 2376 'mozFullScreenElement', 2377 'mozRequestFullScreen', 2378 'mozCancelFullScreen', 2379 'mozfullscreenchange', 2380 'mozfullscreenerror' 2381 ], 2382 // http://developer.apple.com/library/safari/#documentation/WebKit/Reference/ElementClassRef/Element/Element.html 2383 // http://developer.apple.com/library/safari/#documentation/UserExperience/Reference/DocumentAdditionsReference/DocumentAdditions/DocumentAdditions.html 2384 webkit: [ 2385 '', 2386 'webkitCurrentFullScreenElement', 2387 'webkitRequestFullScreen', 2388 'webkitCancelFullScreen', 2389 'webkitfullscreenchange', 2390 '' 2391 ], 2392 // http://developer.apple.com/library/safari/#documentation/AudioVideo/Reference/HTMLVideoElementClassReference/HTMLVideoElement/HTMLVideoElement.html 2393 // https://developer.apple.com/library/safari/samplecode/HTML5VideoEventFlow/Listings/events_js.html#//apple_ref/doc/uid/DTS40010085-events_js-DontLinkElementID_5 2394 // Events: 'webkitbeginfullscreen' and 'webkitendfullscreen' 2395 webkitVideo: [ 2396 'webkitSupportsFullscreen', 2397 'webkitDisplayingFullscreen', 2398 'webkitEnterFullscreen', 2399 'webkitExitFullscreen', 2400 '', 2401 '' 2402 ], 2403 ms: [ 2404 '', 2405 'msFullscreenElement', 2406 'msRequestFullscreen', 2407 'msExitFullscreen', 2408 'MSFullscreenChange', 2409 'MSFullscreenError' 2410 ] 2411 }, 2412 specOrder = [ 2413 'w3c', 2414 'moz', 2415 'webkit', 2416 'webkitVideo', 2417 'ms' 2418 ], 2419 fs, i, il; 2420 2421 this.fullscreen = fs = { 2422 support: { 2423 w3c: !!d[spec.w3c[0]], 2424 moz: !!d[spec.moz[0]], 2425 webkit: typeof d[spec.webkit[3]] === 'function', 2426 webkitVideo: typeof v[spec.webkitVideo[2]] === 'function', 2427 ms: typeof v[spec.ms[2]] === 'function' 2428 }, 2429 used: {} 2430 }; 2431 2432 // Store the name of the spec being used and as a handy boolean. 2433 for(i = 0, il = specOrder.length; i < il; i++) { 2434 var n = specOrder[i]; 2435 if(fs.support[n]) { 2436 fs.spec = n; 2437 fs.used[n] = true; 2438 break; 2439 } 2440 } 2441 2442 if(fs.spec) { 2443 var s = spec[fs.spec]; 2444 fs.api = { 2445 fullscreenEnabled: true, 2446 fullscreenElement: function(elem) { 2447 elem = elem ? elem : d; // Video element required for webkitVideo 2448 return elem[s[1]]; 2449 }, 2450 requestFullscreen: function(elem) { 2451 return elem[s[2]](); // Chrome and Opera want parameter (Element.ALLOW_KEYBOARD_INPUT) but Safari fails if flag used. 2452 }, 2453 exitFullscreen: function(elem) { 2454 elem = elem ? elem : d; // Video element required for webkitVideo 2455 return elem[s[3]](); 2456 } 2457 }; 2458 fs.event = { 2459 fullscreenchange: s[4], 2460 fullscreenerror: s[5] 2461 }; 2462 } else { 2463 fs.api = { 2464 fullscreenEnabled: false, 2465 fullscreenElement: function() { 2466 return null; 2467 }, 2468 requestFullscreen: function() {}, 2469 exitFullscreen: function() {} 2470 }; 2471 fs.event = {}; 2472 } 2473 } 2474 }; 2475 $.jPlayer.nativeFeatures.init(); 2476 2477 // The keyboard control system. 2478 2479 // The current jPlayer instance in focus. 2480 $.jPlayer.focus = null; 2481 2482 // The list of element node names to ignore with key controls. 2483 $.jPlayer.keyIgnoreElementNames = "A INPUT TEXTAREA SELECT BUTTON"; 2484 2485 // The function that deals with key presses. 2486 var keyBindings = function(event) { 2487 var f = $.jPlayer.focus, 2488 ignoreKey; 2489 2490 // A jPlayer instance must be in focus. ie., keyEnabled and the last one played. 2491 if(f) { 2492 // What generated the key press? 2493 $.each( $.jPlayer.keyIgnoreElementNames.split(/\s+/g), function(i, name) { 2494 // The strings should already be uppercase. 2495 if(event.target.nodeName.toUpperCase() === name.toUpperCase()) { 2496 ignoreKey = true; 2497 return false; // exit each. 2498 } 2499 }); 2500 if(!ignoreKey) { 2501 // See if the key pressed matches any of the bindings. 2502 $.each(f.options.keyBindings, function(action, binding) { 2503 // The binding could be a null when the default has been disabled. ie., 1st clause in if() 2504 if( 2505 (binding && $.isFunction(binding.fn)) && 2506 ((typeof binding.key === 'number' && event.which === binding.key) || 2507 (typeof binding.key === 'string' && event.key === binding.key)) 2508 ) { 2509 event.preventDefault(); // Key being used by jPlayer, so prevent default operation. 2510 binding.fn(f); 2511 return false; // exit each. 2512 } 2513 }); 2514 } 2515 } 2516 }; 2517 2518 $.jPlayer.keys = function(en) { 2519 var event = "keydown.jPlayer"; 2520 // Remove any binding, just in case enabled more than once. 2521 $(document.documentElement).unbind(event); 2522 if(en) { 2523 $(document.documentElement).bind(event, keyBindings); 2524 } 2525 }; 2526 2527 // Enable the global key control handler ready for any jPlayer instance with the keyEnabled option enabled. 2528 $.jPlayer.keys(true); 2529 2530 $.jPlayer.prototype = { 2531 count: 0, // Static Variable: Change it via prototype. 2532 version: { // Static Object 2533 script: "2.9.2", 2534 needFlash: "2.9.0", 2535 flash: "unknown" 2536 }, 2537 options: { // Instanced in $.jPlayer() constructor 2538 swfPath: "js", // Path to jquery.jplayer.swf. Can be relative, absolute or server root relative. 2539 solution: "html, flash", // Valid solutions: html, flash, aurora. Order defines priority. 1st is highest, 2540 supplied: "mp3", // Defines which formats jPlayer will try and support and the priority by the order. 1st is highest, 2541 auroraFormats: "wav", // List the aurora.js codecs being loaded externally. Its core supports "wav". Specify format in jPlayer context. EG., The aac.js codec gives the "m4a" format. 2542 preload: 'metadata', // HTML5 Spec values: none, metadata, auto. 2543 volume: 0.8, // The volume. Number 0 to 1. 2544 muted: false, 2545 remainingDuration: false, // When true, the remaining time is shown in the duration GUI element. 2546 toggleDuration: false, // When true, clicks on the duration toggle between the duration and remaining display. 2547 captureDuration: true, // When true, clicks on the duration are captured and no longer propagate up the DOM. 2548 playbackRate: 1, 2549 defaultPlaybackRate: 1, 2550 minPlaybackRate: 0.5, 2551 maxPlaybackRate: 4, 2552 wmode: "opaque", // Valid wmode: window, transparent, opaque, direct, gpu. 2553 backgroundColor: "#000000", // To define the jPlayer div and Flash background color. 2554 cssSelectorAncestor: "#jp_container_1", 2555 cssSelector: { // * denotes properties that should only be required when video media type required. _cssSelector() would require changes to enable splitting these into Audio and Video defaults. 2556 videoPlay: ".jp-video-play", // * 2557 play: ".jp-play", 2558 pause: ".jp-pause", 2559 stop: ".jp-stop", 2560 seekBar: ".jp-seek-bar", 2561 playBar: ".jp-play-bar", 2562 mute: ".jp-mute", 2563 unmute: ".jp-unmute", 2564 volumeBar: ".jp-volume-bar", 2565 volumeBarValue: ".jp-volume-bar-value", 2566 volumeMax: ".jp-volume-max", 2567 playbackRateBar: ".jp-playback-rate-bar", 2568 playbackRateBarValue: ".jp-playback-rate-bar-value", 2569 currentTime: ".jp-current-time", 2570 duration: ".jp-duration", 2571 title: ".jp-title", 2572 fullScreen: ".jp-full-screen", // * 2573 restoreScreen: ".jp-restore-screen", // * 2574 repeat: ".jp-repeat", 2575 repeatOff: ".jp-repeat-off", 2576 gui: ".jp-gui", // The interface used with autohide feature. 2577 noSolution: ".jp-no-solution" // For error feedback when jPlayer cannot find a solution. 2578 }, 2579 stateClass: { // Classes added to the cssSelectorAncestor to indicate the state. 2580 playing: "jp-state-playing", 2581 seeking: "jp-state-seeking", 2582 muted: "jp-state-muted", 2583 looped: "jp-state-looped", 2584 fullScreen: "jp-state-full-screen", 2585 noVolume: "jp-state-no-volume" 2586 }, 2587 useStateClassSkin: false, // A state class skin relies on the state classes to change the visual appearance. The single control toggles the effect, for example: play then pause, mute then unmute. 2588 autoBlur: true, // GUI control handlers will drop focus after clicks. 2589 smoothPlayBar: false, // Smooths the play bar transitions, which affects clicks and short media with big changes per second. 2590 fullScreen: false, // Native Full Screen 2591 fullWindow: false, 2592 autohide: { 2593 restored: false, // Controls the interface autohide feature. 2594 full: true, // Controls the interface autohide feature. 2595 fadeIn: 200, // Milliseconds. The period of the fadeIn anim. 2596 fadeOut: 600, // Milliseconds. The period of the fadeOut anim. 2597 hold: 1000 // Milliseconds. The period of the pause before autohide beings. 2598 }, 2599 loop: false, 2600 repeat: function(event) { // The default jPlayer repeat event handler 2601 if(event.jPlayer.options.loop) { 2602 $(this).unbind(".jPlayerRepeat").bind($.jPlayer.event.ended + ".jPlayer.jPlayerRepeat", function() { 2603 $(this).jPlayer("play"); 2604 }); 2605 } else { 2606 $(this).unbind(".jPlayerRepeat"); 2607 } 2608 }, 2609 nativeVideoControls: { 2610 // Works well on standard browsers. 2611 // Phone and tablet browsers can have problems with the controls disappearing. 2612 }, 2613 noFullWindow: { 2614 msie: /msie [0-6]\./, 2615 ipad: /ipad.*?os [0-4]\./, 2616 iphone: /iphone/, 2617 ipod: /ipod/, 2618 android_pad: /android [0-3]\.(?!.*?mobile)/, 2619 android_phone: /(?=.*android)(?!.*chrome)(?=.*mobile)/, 2620 blackberry: /blackberry/, 2621 windows_ce: /windows ce/, 2622 iemobile: /iemobile/, 2623 webos: /webos/ 2624 }, 2625 noVolume: { 2626 ipad: /ipad/, 2627 iphone: /iphone/, 2628 ipod: /ipod/, 2629 android_pad: /android(?!.*?mobile)/, 2630 android_phone: /android.*?mobile/, 2631 blackberry: /blackberry/, 2632 windows_ce: /windows ce/, 2633 iemobile: /iemobile/, 2634 webos: /webos/, 2635 playbook: /playbook/ 2636 }, 2637 timeFormat: { 2638 // Specific time format for this instance. The supported options are defined in $.jPlayer.timeFormat 2639 // For the undefined options we use the default from $.jPlayer.timeFormat 2640 }, 2641 keyEnabled: false, // Enables keyboard controls. 2642 audioFullScreen: false, // Enables keyboard controls to enter full screen with audio media. 2643 keyBindings: { // The key control object, defining the key codes and the functions to execute. 2644 // The parameter, f = $.jPlayer.focus, will be checked truethy before attempting to call any of these functions. 2645 // Properties may be added to this object, in key/fn pairs, to enable other key controls. EG, for the playlist add-on. 2646 play: { 2647 key: 80, // p 2648 fn: function(f) { 2649 if(f.status.paused) { 2650 f.play(); 2651 } else { 2652 f.pause(); 2653 } 2654 } 2655 }, 2656 fullScreen: { 2657 key: 70, // f 2658 fn: function(f) { 2659 if(f.status.video || f.options.audioFullScreen) { 2660 f._setOption("fullScreen", !f.options.fullScreen); 2661 } 2662 } 2663 }, 2664 muted: { 2665 key: 77, // m 2666 fn: function(f) { 2667 f._muted(!f.options.muted); 2668 } 2669 }, 2670 volumeUp: { 2671 key: 190, // . 2672 fn: function(f) { 2673 f.volume(f.options.volume + 0.1); 2674 } 2675 }, 2676 volumeDown: { 2677 key: 188, // , 2678 fn: function(f) { 2679 f.volume(f.options.volume - 0.1); 2680 } 2681 }, 2682 loop: { 2683 key: 76, // l 2684 fn: function(f) { 2685 f._loop(!f.options.loop); 2686 } 2687 } 2688 }, 2689 verticalVolume: false, // Calculate volume from the bottom of the volume bar. Default is from the left. Also volume affects either width or height. 2690 verticalPlaybackRate: false, 2691 globalVolume: false, // Set to make volume and muted changes affect all jPlayer instances with this option enabled 2692 idPrefix: "jp", // Prefix for the ids of html elements created by jPlayer. For flash, this must not include characters: . - + * / \ 2693 noConflict: "jQuery", 2694 emulateHtml: false, // Emulates the HTML5 Media element on the jPlayer element. 2695 consoleAlerts: true, // Alerts are sent to the console.log() instead of alert(). 2696 errorAlerts: false, 2697 warningAlerts: false 2698 }, 2699 optionsAudio: { 2700 size: { 2701 width: "0px", 2702 height: "0px", 2703 cssClass: "" 2704 }, 2705 sizeFull: { 2706 width: "0px", 2707 height: "0px", 2708 cssClass: "" 2709 } 2710 }, 2711 optionsVideo: { 2712 size: { 2713 width: "480px", 2714 height: "270px", 2715 cssClass: "jp-video-270p" 2716 }, 2717 sizeFull: { 2718 width: "100%", 2719 height: "100%", 2720 cssClass: "jp-video-full" 2721 } 2722 }, 2723 instances: {}, // Static Object 2724 status: { // Instanced in _init() 2725 src: "", 2726 media: {}, 2727 paused: true, 2728 format: {}, 2729 formatType: "", 2730 waitForPlay: true, // Same as waitForLoad except in case where preloading. 2731 waitForLoad: true, 2732 srcSet: false, 2733 video: false, // True if playing a video 2734 seekPercent: 0, 2735 currentPercentRelative: 0, 2736 currentPercentAbsolute: 0, 2737 currentTime: 0, 2738 duration: 0, 2739 remaining: 0, 2740 videoWidth: 0, // Intrinsic width of the video in pixels. 2741 videoHeight: 0, // Intrinsic height of the video in pixels. 2742 readyState: 0, 2743 networkState: 0, 2744 playbackRate: 1, // Warning - Now both an option and a status property 2745 ended: 0 2746 2747/* Persistant status properties created dynamically at _init(): 2748 width 2749 height 2750 cssClass 2751 nativeVideoControls 2752 noFullWindow 2753 noVolume 2754 playbackRateEnabled // Warning - Technically, we can have both Flash and HTML, so this might not be correct if the Flash is active. That is a niche case. 2755*/ 2756 }, 2757 2758 internal: { // Instanced in _init() 2759 ready: false 2760 // instance: undefined 2761 // domNode: undefined 2762 // htmlDlyCmdId: undefined 2763 // autohideId: undefined 2764 // mouse: undefined 2765 // cmdsIgnored 2766 }, 2767 solution: { // Static Object: Defines the solutions built in jPlayer. 2768 html: true, 2769 aurora: true, 2770 flash: true 2771 }, 2772 // 'MPEG-4 support' : canPlayType('video/mp4; codecs="mp4v.20.8"') 2773 format: { // Static Object 2774 mp3: { 2775 codec: 'audio/mpeg', 2776 flashCanPlay: true, 2777 media: 'audio' 2778 }, 2779 m4a: { // AAC / MP4 2780 codec: 'audio/mp4; codecs="mp4a.40.2"', 2781 flashCanPlay: true, 2782 media: 'audio' 2783 }, 2784 m3u8a: { // AAC / MP4 / Apple HLS 2785 codec: 'application/vnd.apple.mpegurl; codecs="mp4a.40.2"', 2786 flashCanPlay: false, 2787 media: 'audio' 2788 }, 2789 m3ua: { // M3U 2790 codec: 'audio/mpegurl', 2791 flashCanPlay: false, 2792 media: 'audio' 2793 }, 2794 oga: { // OGG 2795 codec: 'audio/ogg; codecs="vorbis, opus"', 2796 flashCanPlay: false, 2797 media: 'audio' 2798 }, 2799 flac: { // FLAC 2800 codec: 'audio/x-flac', 2801 flashCanPlay: false, 2802 media: 'audio' 2803 }, 2804 wav: { // PCM 2805 codec: 'audio/wav; codecs="1"', 2806 flashCanPlay: false, 2807 media: 'audio' 2808 }, 2809 webma: { // WEBM 2810 codec: 'audio/webm; codecs="vorbis"', 2811 flashCanPlay: false, 2812 media: 'audio' 2813 }, 2814 fla: { // FLV / F4A 2815 codec: 'audio/x-flv', 2816 flashCanPlay: true, 2817 media: 'audio' 2818 }, 2819 rtmpa: { // RTMP AUDIO 2820 codec: 'audio/rtmp; codecs="rtmp"', 2821 flashCanPlay: true, 2822 media: 'audio' 2823 }, 2824 m4v: { // H.264 / MP4 2825 codec: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"', 2826 flashCanPlay: true, 2827 media: 'video' 2828 }, 2829 m3u8v: { // H.264 / AAC / MP4 / Apple HLS 2830 codec: 'application/vnd.apple.mpegurl; codecs="avc1.42E01E, mp4a.40.2"', 2831 flashCanPlay: false, 2832 media: 'video' 2833 }, 2834 m3uv: { // M3U 2835 codec: 'audio/mpegurl', 2836 flashCanPlay: false, 2837 media: 'video' 2838 }, 2839 ogv: { // OGG 2840 codec: 'video/ogg; codecs="theora, vorbis"', 2841 flashCanPlay: false, 2842 media: 'video' 2843 }, 2844 webmv: { // WEBM 2845 codec: 'video/webm; codecs="vorbis, vp8"', 2846 flashCanPlay: false, 2847 media: 'video' 2848 }, 2849 flv: { // FLV / F4V 2850 codec: 'video/x-flv', 2851 flashCanPlay: true, 2852 media: 'video' 2853 }, 2854 rtmpv: { // RTMP VIDEO 2855 codec: 'video/rtmp; codecs="rtmp"', 2856 flashCanPlay: true, 2857 media: 'video' 2858 } 2859 }, 2860 _init: function() { 2861 var self = this; 2862 2863 this.element.empty(); 2864 2865 this.status = $.extend({}, this.status); // Copy static to unique instance. 2866 this.internal = $.extend({}, this.internal); // Copy static to unique instance. 2867 2868 // Initialize the time format 2869 this.options.timeFormat = $.extend({}, $.jPlayer.timeFormat, this.options.timeFormat); 2870 2871 // On iOS, assume commands will be ignored before user initiates them. 2872 this.internal.cmdsIgnored = $.jPlayer.platform.ipad || $.jPlayer.platform.iphone || $.jPlayer.platform.ipod; 2873 2874 this.internal.domNode = this.element.get(0); 2875 2876 // Add key bindings focus to 1st jPlayer instanced with key control enabled. 2877 if(this.options.keyEnabled && !$.jPlayer.focus) { 2878 $.jPlayer.focus = this; 2879 } 2880 2881 // A fix for Android where older (2.3) and even some 4.x devices fail to work when changing the *audio* SRC and then playing immediately. 2882 this.androidFix = { 2883 setMedia: false, // True when media set 2884 play: false, // True when a progress event will instruct the media to play 2885 pause: false, // True when a progress event will instruct the media to pause at a time. 2886 time: NaN // The play(time) parameter 2887 }; 2888 if($.jPlayer.platform.android) { 2889 this.options.preload = this.options.preload !== 'auto' ? 'metadata' : 'auto'; // Default to metadata, but allow auto. 2890 } 2891 2892 this.formats = []; // Array based on supplied string option. Order defines priority. 2893 this.solutions = []; // Array based on solution string option. Order defines priority. 2894 this.require = {}; // Which media types are required: video, audio. 2895 2896 this.htmlElement = {}; // DOM elements created by jPlayer 2897 this.html = {}; // In _init()'s this.desired code and setmedia(): Accessed via this[solution], where solution from this.solutions array. 2898 this.html.audio = {}; 2899 this.html.video = {}; 2900 this.aurora = {}; // In _init()'s this.desired code and setmedia(): Accessed via this[solution], where solution from this.solutions array. 2901 this.aurora.formats = []; 2902 this.aurora.properties = []; 2903 this.flash = {}; // In _init()'s this.desired code and setmedia(): Accessed via this[solution], where solution from this.solutions array. 2904 2905 this.css = {}; 2906 this.css.cs = {}; // Holds the css selector strings 2907 this.css.jq = {}; // Holds jQuery selectors. ie., $(css.cs.method) 2908 2909 this.ancestorJq = []; // Holds jQuery selector of cssSelectorAncestor. Init would use $() instead of [], but it is only 1.4+ 2910 2911 this.options.volume = this._limitValue(this.options.volume, 0, 1); // Limit volume value's bounds. 2912 2913 // Create the formats array, with prority based on the order of the supplied formats string 2914 $.each(this.options.supplied.toLowerCase().split(","), function(index1, value1) { 2915 var format = value1.replace(/^\s+|\s+$/g, ""); //trim 2916 if(self.format[format]) { // Check format is valid. 2917 var dupFound = false; 2918 $.each(self.formats, function(index2, value2) { // Check for duplicates 2919 if(format === value2) { 2920 dupFound = true; 2921 return false; 2922 } 2923 }); 2924 if(!dupFound) { 2925 self.formats.push(format); 2926 } 2927 } 2928 }); 2929 2930 // Create the solutions array, with prority based on the order of the solution string 2931 $.each(this.options.solution.toLowerCase().split(","), function(index1, value1) { 2932 var solution = value1.replace(/^\s+|\s+$/g, ""); //trim 2933 if(self.solution[solution]) { // Check solution is valid. 2934 var dupFound = false; 2935 $.each(self.solutions, function(index2, value2) { // Check for duplicates 2936 if(solution === value2) { 2937 dupFound = true; 2938 return false; 2939 } 2940 }); 2941 if(!dupFound) { 2942 self.solutions.push(solution); 2943 } 2944 } 2945 }); 2946 2947 // Create Aurora.js formats array 2948 $.each(this.options.auroraFormats.toLowerCase().split(","), function(index1, value1) { 2949 var format = value1.replace(/^\s+|\s+$/g, ""); //trim 2950 if(self.format[format]) { // Check format is valid. 2951 var dupFound = false; 2952 $.each(self.aurora.formats, function(index2, value2) { // Check for duplicates 2953 if(format === value2) { 2954 dupFound = true; 2955 return false; 2956 } 2957 }); 2958 if(!dupFound) { 2959 self.aurora.formats.push(format); 2960 } 2961 } 2962 }); 2963 2964 this.internal.instance = "jp_" + this.count; 2965 this.instances[this.internal.instance] = this.element; 2966 2967 // Check the jPlayer div has an id and create one if required. Important for Flash to know the unique id for comms. 2968 if(!this.element.attr("id")) { 2969 this.element.attr("id", this.options.idPrefix + "_jplayer_" + this.count); 2970 } 2971 2972 this.internal.self = $.extend({}, { 2973 id: this.element.attr("id"), 2974 jq: this.element 2975 }); 2976 this.internal.audio = $.extend({}, { 2977 id: this.options.idPrefix + "_audio_" + this.count, 2978 jq: undefined 2979 }); 2980 this.internal.video = $.extend({}, { 2981 id: this.options.idPrefix + "_video_" + this.count, 2982 jq: undefined 2983 }); 2984 this.internal.flash = $.extend({}, { 2985 id: this.options.idPrefix + "_flash_" + this.count, 2986 jq: undefined, 2987 swf: this.options.swfPath + (this.options.swfPath.toLowerCase().slice(-4) !== ".swf" ? (this.options.swfPath && this.options.swfPath.slice(-1) !== "/" ? "/" : "") + "jquery.jplayer.swf" : "") 2988 }); 2989 this.internal.poster = $.extend({}, { 2990 id: this.options.idPrefix + "_poster_" + this.count, 2991 jq: undefined 2992 }); 2993 2994 // Register listeners defined in the constructor 2995 $.each($.jPlayer.event, function(eventName,eventType) { 2996 if(self.options[eventName] !== undefined) { 2997 self.element.bind(eventType + ".jPlayer", self.options[eventName]); // With .jPlayer namespace. 2998 self.options[eventName] = undefined; // Destroy the handler pointer copy on the options. Reason, events can be added/removed in other ways so this could be obsolete and misleading. 2999 } 3000 }); 3001 3002 // Determine if we require solutions for audio, video or both media types. 3003 this.require.audio = false; 3004 this.require.video = false; 3005 $.each(this.formats, function(priority, format) { 3006 self.require[self.format[format].media] = true; 3007 }); 3008 3009 // Now required types are known, finish the options default settings. 3010 if(this.require.video) { 3011 this.options = $.extend(true, {}, 3012 this.optionsVideo, 3013 this.options 3014 ); 3015 } else { 3016 this.options = $.extend(true, {}, 3017 this.optionsAudio, 3018 this.options 3019 ); 3020 } 3021 this._setSize(); // update status and jPlayer element size 3022 3023 // Determine the status for Blocklisted options. 3024 this.status.nativeVideoControls = this._uaBlocklist(this.options.nativeVideoControls); 3025 this.status.noFullWindow = this._uaBlocklist(this.options.noFullWindow); 3026 this.status.noVolume = this._uaBlocklist(this.options.noVolume); 3027 3028 // Create event handlers if native fullscreen is supported 3029 if($.jPlayer.nativeFeatures.fullscreen.api.fullscreenEnabled) { 3030 this._fullscreenAddEventListeners(); 3031 } 3032 3033 // The native controls are only for video and are disabled when audio is also used. 3034 this._restrictNativeVideoControls(); 3035 3036 // Create the poster image. 3037 this.htmlElement.poster = document.createElement('img'); 3038 this.htmlElement.poster.id = this.internal.poster.id; 3039 this.htmlElement.poster.onload = function() { // Note that this did not work on Firefox 3.6: poster.addEventListener("onload", function() {}, false); Did not investigate x-browser. 3040 if(!self.status.video || self.status.waitForPlay) { 3041 self.internal.poster.jq.show(); 3042 } 3043 }; 3044 this.element.append(this.htmlElement.poster); 3045 this.internal.poster.jq = $("#" + this.internal.poster.id); 3046 this.internal.poster.jq.css({'width': this.status.width, 'height': this.status.height}); 3047 this.internal.poster.jq.hide(); 3048 this.internal.poster.jq.bind("click.jPlayer", function() { 3049 self._trigger($.jPlayer.event.click); 3050 }); 3051 3052 // Generate the required media elements 3053 this.html.audio.available = false; 3054 if(this.require.audio) { // If a supplied format is audio 3055 this.htmlElement.audio = document.createElement('audio'); 3056 this.htmlElement.audio.id = this.internal.audio.id; 3057 this.html.audio.available = !!this.htmlElement.audio.canPlayType && this._testCanPlayType(this.htmlElement.audio); // Test is for IE9 on Win Server 2008. 3058 } 3059 this.html.video.available = false; 3060 if(this.require.video) { // If a supplied format is video 3061 this.htmlElement.video = document.createElement('video'); 3062 this.htmlElement.video.id = this.internal.video.id; 3063 this.html.video.available = !!this.htmlElement.video.canPlayType && this._testCanPlayType(this.htmlElement.video); // Test is for IE9 on Win Server 2008. 3064 } 3065 3066 this.flash.available = this._checkForFlash(10.1); 3067 3068 this.html.canPlay = {}; 3069 this.aurora.canPlay = {}; 3070 this.flash.canPlay = {}; 3071 $.each(this.formats, function(priority, format) { 3072 self.html.canPlay[format] = self.html[self.format[format].media].available && "" !== self.htmlElement[self.format[format].media].canPlayType(self.format[format].codec); 3073 self.aurora.canPlay[format] = ($.inArray(format, self.aurora.formats) > -1); 3074 self.flash.canPlay[format] = self.format[format].flashCanPlay && self.flash.available; 3075 }); 3076 this.html.desired = false; 3077 this.aurora.desired = false; 3078 this.flash.desired = false; 3079 $.each(this.solutions, function(solutionPriority, solution) { 3080 if(solutionPriority === 0) { 3081 self[solution].desired = true; 3082 } else { 3083 var audioCanPlay = false; 3084 var videoCanPlay = false; 3085 $.each(self.formats, function(formatPriority, format) { 3086 if(self[self.solutions[0]].canPlay[format]) { // The other solution can play 3087 if(self.format[format].media === 'video') { 3088 videoCanPlay = true; 3089 } else { 3090 audioCanPlay = true; 3091 } 3092 } 3093 }); 3094 self[solution].desired = (self.require.audio && !audioCanPlay) || (self.require.video && !videoCanPlay); 3095 } 3096 }); 3097 // This is what jPlayer will support, based on solution and supplied. 3098 this.html.support = {}; 3099 this.aurora.support = {}; 3100 this.flash.support = {}; 3101 $.each(this.formats, function(priority, format) { 3102 self.html.support[format] = self.html.canPlay[format] && self.html.desired; 3103 self.aurora.support[format] = self.aurora.canPlay[format] && self.aurora.desired; 3104 self.flash.support[format] = self.flash.canPlay[format] && self.flash.desired; 3105 }); 3106 // If jPlayer is supporting any format in a solution, then the solution is used. 3107 this.html.used = false; 3108 this.aurora.used = false; 3109 this.flash.used = false; 3110 $.each(this.solutions, function(solutionPriority, solution) { 3111 $.each(self.formats, function(formatPriority, format) { 3112 if(self[solution].support[format]) { 3113 self[solution].used = true; 3114 return false; 3115 } 3116 }); 3117 }); 3118 3119 // Init solution active state and the event gates to false. 3120 this._resetActive(); 3121 this._resetGate(); 3122 3123 // Set up the css selectors for the control and feedback entities. 3124 this._cssSelectorAncestor(this.options.cssSelectorAncestor); 3125 3126 // If neither html nor aurora nor flash are being used by this browser, then media playback is not possible. Trigger an error event. 3127 if(!(this.html.used || this.aurora.used || this.flash.used)) { 3128 this._error( { 3129 type: $.jPlayer.error.NO_SOLUTION, 3130 context: "{solution:'" + this.options.solution + "', supplied:'" + this.options.supplied + "'}", 3131 message: $.jPlayer.errorMsg.NO_SOLUTION, 3132 hint: $.jPlayer.errorHint.NO_SOLUTION 3133 }); 3134 if(this.css.jq.noSolution.length) { 3135 this.css.jq.noSolution.show(); 3136 } 3137 } else { 3138 if(this.css.jq.noSolution.length) { 3139 this.css.jq.noSolution.hide(); 3140 } 3141 } 3142 3143 // Add the flash solution if it is being used. 3144 if(this.flash.used) { 3145 var htmlObj, 3146 flashVars = 'jQuery=' + encodeURI(this.options.noConflict) + '&id=' + encodeURI(this.internal.self.id) + '&vol=' + this.options.volume + '&muted=' + this.options.muted; 3147 3148 // Code influenced by SWFObject 2.2: http://code.google.com/p/swfobject/ 3149 // Non IE browsers have an initial Flash size of 1 by 1 otherwise the wmode affected the Flash ready event. 3150 3151 if($.jPlayer.browser.msie && (Number($.jPlayer.browser.version) < 9 || $.jPlayer.browser.documentMode < 9)) { 3152 var objStr = '<object id="' + this.internal.flash.id + '" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="0" height="0" tabindex="-1"></object>'; 3153 3154 var paramStr = [ 3155 '<param name="movie" value="' + this.internal.flash.swf + '" />', 3156 '<param name="FlashVars" value="' + flashVars + '" />', 3157 '<param name="allowScriptAccess" value="always" />', 3158 '<param name="bgcolor" value="' + this.options.backgroundColor + '" />', 3159 '<param name="wmode" value="' + this.options.wmode + '" />' 3160 ]; 3161 3162 htmlObj = document.createElement(objStr); 3163 for(var i=0; i < paramStr.length; i++) { 3164 htmlObj.appendChild(document.createElement(paramStr[i])); 3165 } 3166 } else { 3167 var createParam = function(el, n, v) { 3168 var p = document.createElement("param"); 3169 p.setAttribute("name", n); 3170 p.setAttribute("value", v); 3171 el.appendChild(p); 3172 }; 3173 3174 htmlObj = document.createElement("object"); 3175 htmlObj.setAttribute("id", this.internal.flash.id); 3176 htmlObj.setAttribute("name", this.internal.flash.id); 3177 htmlObj.setAttribute("data", this.internal.flash.swf); 3178 htmlObj.setAttribute("type", "application/x-shockwave-flash"); 3179 htmlObj.setAttribute("width", "1"); // Non-zero 3180 htmlObj.setAttribute("height", "1"); // Non-zero 3181 htmlObj.setAttribute("tabindex", "-1"); 3182 createParam(htmlObj, "flashvars", flashVars); 3183 createParam(htmlObj, "allowscriptaccess", "always"); 3184 createParam(htmlObj, "bgcolor", this.options.backgroundColor); 3185 createParam(htmlObj, "wmode", this.options.wmode); 3186 } 3187 3188 this.element.append(htmlObj); 3189 this.internal.flash.jq = $(htmlObj); 3190 } 3191 3192 // Setup playbackRate ability before using _addHtmlEventListeners() 3193 if(this.html.used && !this.flash.used) { // If only HTML 3194 // Using the audio element capabilities for playbackRate. ie., Assuming video element is the same. 3195 this.status.playbackRateEnabled = this._testPlaybackRate('audio'); 3196 } else { 3197 this.status.playbackRateEnabled = false; 3198 } 3199 3200 this._updatePlaybackRate(); 3201 3202 // Add the HTML solution if being used. 3203 if(this.html.used) { 3204 3205 // The HTML Audio handlers 3206 if(this.html.audio.available) { 3207 this._addHtmlEventListeners(this.htmlElement.audio, this.html.audio); 3208 this.element.append(this.htmlElement.audio); 3209 this.internal.audio.jq = $("#" + this.internal.audio.id); 3210 } 3211 3212 // The HTML Video handlers 3213 if(this.html.video.available) { 3214 this._addHtmlEventListeners(this.htmlElement.video, this.html.video); 3215 this.element.append(this.htmlElement.video); 3216 this.internal.video.jq = $("#" + this.internal.video.id); 3217 if(this.status.nativeVideoControls) { 3218 this.internal.video.jq.css({'width': this.status.width, 'height': this.status.height}); 3219 } else { 3220 this.internal.video.jq.css({'width':'0px', 'height':'0px'}); // Using size 0x0 since a .hide() causes issues in iOS 3221 } 3222 this.internal.video.jq.bind("click.jPlayer", function() { 3223 self._trigger($.jPlayer.event.click); 3224 }); 3225 } 3226 } 3227 3228 // Add the Aurora.js solution if being used. 3229 if(this.aurora.used) { 3230 // Aurora.js player need to be created for each media, see setMedia function. 3231 } 3232 3233 // Create the bridge that emulates the HTML Media element on the jPlayer DIV 3234 if( this.options.emulateHtml ) { 3235 this._emulateHtmlBridge(); 3236 } 3237 3238 if((this.html.used || this.aurora.used) && !this.flash.used) { // If only HTML, then emulate flash ready() call after 100ms. 3239 setTimeout( function() { 3240 self.internal.ready = true; 3241 self.version.flash = "n/a"; 3242 self._trigger($.jPlayer.event.repeat); // Trigger the repeat event so its handler can initialize itself with the loop option. 3243 self._trigger($.jPlayer.event.ready); 3244 }, 100); 3245 } 3246 3247 // Initialize the interface components with the options. 3248 this._updateNativeVideoControls(); 3249 // The other controls are now setup in _cssSelectorAncestor() 3250 if(this.css.jq.videoPlay.length) { 3251 this.css.jq.videoPlay.hide(); 3252 } 3253 3254 $.jPlayer.prototype.count++; // Change static variable via prototype. 3255 }, 3256 destroy: function() { 3257 // MJP: The background change remains. Would need to store the original to restore it correctly. 3258 // MJP: The jPlayer element's size change remains. 3259 3260 // Clear the media to reset the GUI and stop any downloads. Streams on some browsers had persited. (Chrome) 3261 this.clearMedia(); 3262 // Remove the size/sizeFull cssClass from the cssSelectorAncestor 3263 this._removeUiClass(); 3264 // Remove the times from the GUI 3265 if(this.css.jq.currentTime.length) { 3266 this.css.jq.currentTime.text(""); 3267 } 3268 if(this.css.jq.duration.length) { 3269 this.css.jq.duration.text(""); 3270 } 3271 // Remove any bindings from the interface controls. 3272 $.each(this.css.jq, function(fn, jq) { 3273 // Check selector is valid before trying to execute method. 3274 if(jq.length) { 3275 jq.unbind(".jPlayer"); 3276 } 3277 }); 3278 // Remove the click handlers for $.jPlayer.event.click 3279 this.internal.poster.jq.unbind(".jPlayer"); 3280 if(this.internal.video.jq) { 3281 this.internal.video.jq.unbind(".jPlayer"); 3282 } 3283 // Remove the fullscreen event handlers 3284 this._fullscreenRemoveEventListeners(); 3285 // Remove key bindings 3286 if(this === $.jPlayer.focus) { 3287 $.jPlayer.focus = null; 3288 } 3289 // Destroy the HTML bridge. 3290 if(this.options.emulateHtml) { 3291 this._destroyHtmlBridge(); 3292 } 3293 this.element.removeData("jPlayer"); // Remove jPlayer data 3294 this.element.unbind(".jPlayer"); // Remove all event handlers created by the jPlayer constructor 3295 this.element.empty(); // Remove the inserted child elements 3296 3297 delete this.instances[this.internal.instance]; // Clear the instance on the static instance object 3298 }, 3299 destroyRemoved: function() { // Destroy any instances that have gone away. 3300 var self = this; 3301 $.each(this.instances, function(i, element) { 3302 if(self.element !== element) { // Do not destroy this instance. 3303 if(!element.data("jPlayer")) { // Check that element is a real jPlayer. 3304 element.jPlayer("destroy"); 3305 delete self.instances[i]; 3306 } 3307 } 3308 }); 3309 }, 3310 enable: function() { // Plan to implement 3311 // options.disabled = false 3312 }, 3313 disable: function () { // Plan to implement 3314 // options.disabled = true 3315 }, 3316 _testCanPlayType: function(elem) { 3317 // IE9 on Win Server 2008 did not implement canPlayType(), but it has the property. 3318 try { 3319 elem.canPlayType(this.format.mp3.codec); // The type is irrelevant. 3320 return true; 3321 } catch(err) { 3322 return false; 3323 } 3324 }, 3325 _testPlaybackRate: function(type) { 3326 // type: String 'audio' or 'video' 3327 var el, rate = 0.5; 3328 type = typeof type === 'string' ? type : 'audio'; 3329 el = document.createElement(type); 3330 // Wrapping in a try/catch, just in case older HTML5 browsers throw and error. 3331 try { 3332 if('playbackRate' in el) { 3333 el.playbackRate = rate; 3334 return el.playbackRate === rate; 3335 } else { 3336 return false; 3337 } 3338 } catch(err) { 3339 return false; 3340 } 3341 }, 3342 _uaBlocklist: function(list) { 3343 // list : object with properties that are all regular expressions. Property names are irrelevant. 3344 // Returns true if the user agent is matched in list. 3345 var ua = navigator.userAgent.toLowerCase(), 3346 block = false; 3347 3348 $.each(list, function(p, re) { 3349 if(re && re.test(ua)) { 3350 block = true; 3351 return false; // exit $.each. 3352 } 3353 }); 3354 return block; 3355 }, 3356 _restrictNativeVideoControls: function() { 3357 // Fallback to noFullWindow when nativeVideoControls is true and audio media is being used. Affects when both media types are used. 3358 if(this.require.audio) { 3359 if(this.status.nativeVideoControls) { 3360 this.status.nativeVideoControls = false; 3361 this.status.noFullWindow = true; 3362 } 3363 } 3364 }, 3365 _updateNativeVideoControls: function() { 3366 if(this.html.video.available && this.html.used) { 3367 // Turn the HTML Video controls on/off 3368 this.htmlElement.video.controls = this.status.nativeVideoControls; 3369 // Show/hide the jPlayer GUI. 3370 this._updateAutohide(); 3371 // For when option changed. The poster image is not updated, as it is dealt with in setMedia(). Acceptable degradation since seriously doubt these options will change on the fly. Can again review later. 3372 if(this.status.nativeVideoControls && this.require.video) { 3373 this.internal.poster.jq.hide(); 3374 this.internal.video.jq.css({'width': this.status.width, 'height': this.status.height}); 3375 } else if(this.status.waitForPlay && this.status.video) { 3376 this.internal.poster.jq.show(); 3377 this.internal.video.jq.css({'width': '0px', 'height': '0px'}); 3378 } 3379 } 3380 }, 3381 _addHtmlEventListeners: function(mediaElement, entity) { 3382 var self = this; 3383 mediaElement.preload = this.options.preload; 3384 mediaElement.muted = this.options.muted; 3385 mediaElement.volume = this.options.volume; 3386 3387 if(this.status.playbackRateEnabled) { 3388 mediaElement.defaultPlaybackRate = this.options.defaultPlaybackRate; 3389 mediaElement.playbackRate = this.options.playbackRate; 3390 } 3391 3392 // Create the event listeners 3393 // Only want the active entity to affect jPlayer and bubble events. 3394 // Using entity.gate so that object is referenced and gate property always current 3395 3396 mediaElement.addEventListener("progress", function() { 3397 if(entity.gate) { 3398 if(self.internal.cmdsIgnored && this.readyState > 0) { // Detect iOS executed the command 3399 self.internal.cmdsIgnored = false; 3400 } 3401 self._getHtmlStatus(mediaElement); 3402 self._updateInterface(); 3403 self._trigger($.jPlayer.event.progress); 3404 } 3405 }, false); 3406 mediaElement.addEventListener("loadeddata", function() { 3407 if(entity.gate) { 3408 self.androidFix.setMedia = false; // Disable the fix after the first progress event. 3409 if(self.androidFix.play) { // Play Android audio - performing the fix. 3410 self.androidFix.play = false; 3411 self.play(self.androidFix.time); 3412 } 3413 if(self.androidFix.pause) { // Pause Android audio at time - performing the fix. 3414 self.androidFix.pause = false; 3415 self.pause(self.androidFix.time); 3416 } 3417 self._trigger($.jPlayer.event.loadeddata); 3418 } 3419 }, false); 3420 mediaElement.addEventListener("timeupdate", function() { 3421 if(entity.gate) { 3422 self._getHtmlStatus(mediaElement); 3423 self._updateInterface(); 3424 self._trigger($.jPlayer.event.timeupdate); 3425 } 3426 }, false); 3427 mediaElement.addEventListener("durationchange", function() { 3428 if(entity.gate) { 3429 self._getHtmlStatus(mediaElement); 3430 self._updateInterface(); 3431 self._trigger($.jPlayer.event.durationchange); 3432 } 3433 }, false); 3434 mediaElement.addEventListener("play", function() { 3435 if(entity.gate) { 3436 self._updateButtons(true); 3437 self._html_checkWaitForPlay(); // So the native controls update this variable and puts the hidden interface in the correct state. Affects toggling native controls. 3438 self._trigger($.jPlayer.event.play); 3439 } 3440 }, false); 3441 mediaElement.addEventListener("playing", function() { 3442 if(entity.gate) { 3443 self._updateButtons(true); 3444 self._seeked(); 3445 self._trigger($.jPlayer.event.playing); 3446 } 3447 }, false); 3448 mediaElement.addEventListener("pause", function() { 3449 if(entity.gate) { 3450 self._updateButtons(false); 3451 self._trigger($.jPlayer.event.pause); 3452 } 3453 }, false); 3454 mediaElement.addEventListener("waiting", function() { 3455 if(entity.gate) { 3456 self._seeking(); 3457 self._trigger($.jPlayer.event.waiting); 3458 } 3459 }, false); 3460 mediaElement.addEventListener("seeking", function() { 3461 if(entity.gate) { 3462 self._seeking(); 3463 self._trigger($.jPlayer.event.seeking); 3464 } 3465 }, false); 3466 mediaElement.addEventListener("seeked", function() { 3467 if(entity.gate) { 3468 self._seeked(); 3469 self._trigger($.jPlayer.event.seeked); 3470 } 3471 }, false); 3472 mediaElement.addEventListener("volumechange", function() { 3473 if(entity.gate) { 3474 // Read the values back from the element as the Blackberry PlayBook shares the volume with the physical buttons master volume control. 3475 // However, when tested 6th July 2011, those buttons do not generate an event. The physical play/pause button does though. 3476 self.options.volume = mediaElement.volume; 3477 self.options.muted = mediaElement.muted; 3478 self._updateMute(); 3479 self._updateVolume(); 3480 self._trigger($.jPlayer.event.volumechange); 3481 } 3482 }, false); 3483 mediaElement.addEventListener("ratechange", function() { 3484 if(entity.gate) { 3485 self.options.defaultPlaybackRate = mediaElement.defaultPlaybackRate; 3486 self.options.playbackRate = mediaElement.playbackRate; 3487 self._updatePlaybackRate(); 3488 self._trigger($.jPlayer.event.ratechange); 3489 } 3490 }, false); 3491 mediaElement.addEventListener("suspend", function() { // Seems to be the only way of capturing that the iOS4 browser did not actually play the media from the page code. ie., It needs a user gesture. 3492 if(entity.gate) { 3493 self._seeked(); 3494 self._trigger($.jPlayer.event.suspend); 3495 } 3496 }, false); 3497 mediaElement.addEventListener("ended", function() { 3498 if(entity.gate) { 3499 // Order of the next few commands are important. Change the time and then pause. 3500 // Solves a bug in Firefox, where issuing pause 1st causes the media to play from the start. ie., The pause is ignored. 3501 if(!$.jPlayer.browser.webkit) { // Chrome crashes if you do this in conjunction with a setMedia command in an ended event handler. ie., The playlist demo. 3502 self.htmlElement.media.currentTime = 0; // Safari does not care about this command. ie., It works with or without this line. (Both Safari and Chrome are Webkit.) 3503 } 3504 self.htmlElement.media.pause(); // Pause otherwise a click on the progress bar will play from that point, when it shouldn't, since it stopped playback. 3505 self._updateButtons(false); 3506 self._getHtmlStatus(mediaElement, true); // With override true. Otherwise Chrome leaves progress at full. 3507 self._updateInterface(); 3508 self._trigger($.jPlayer.event.ended); 3509 } 3510 }, false); 3511 mediaElement.addEventListener("error", function() { 3512 if(entity.gate) { 3513 self._updateButtons(false); 3514 self._seeked(); 3515 if(self.status.srcSet) { // Deals with case of clearMedia() causing an error event. 3516 clearTimeout(self.internal.htmlDlyCmdId); // Clears any delayed commands used in the HTML solution. 3517 self.status.waitForLoad = true; // Allows the load operation to try again. 3518 self.status.waitForPlay = true; // Reset since a play was captured. 3519 if(self.status.video && !self.status.nativeVideoControls) { 3520 self.internal.video.jq.css({'width':'0px', 'height':'0px'}); 3521 } 3522 if(self._validString(self.status.media.poster) && !self.status.nativeVideoControls) { 3523 self.internal.poster.jq.show(); 3524 } 3525 if(self.css.jq.videoPlay.length) { 3526 self.css.jq.videoPlay.show(); 3527 } 3528 self._error( { 3529 type: $.jPlayer.error.URL, 3530 context: self.status.src, // this.src shows absolute urls. Want context to show the url given. 3531 message: $.jPlayer.errorMsg.URL, 3532 hint: $.jPlayer.errorHint.URL 3533 }); 3534 } 3535 } 3536 }, false); 3537 // Create all the other event listeners that bubble up to a jPlayer event from html, without being used by jPlayer. 3538 $.each($.jPlayer.htmlEvent, function(i, eventType) { 3539 mediaElement.addEventListener(this, function() { 3540 if(entity.gate) { 3541 self._trigger($.jPlayer.event[eventType]); 3542 } 3543 }, false); 3544 }); 3545 }, 3546 _addAuroraEventListeners : function(player, entity) { 3547 var self = this; 3548 //player.preload = this.options.preload; 3549 //player.muted = this.options.muted; 3550 player.volume = this.options.volume * 100; 3551 3552 // Create the event listeners 3553 // Only want the active entity to affect jPlayer and bubble events. 3554 // Using entity.gate so that object is referenced and gate property always current 3555 3556 player.on("progress", function() { 3557 if(entity.gate) { 3558 if(self.internal.cmdsIgnored && this.readyState > 0) { // Detect iOS executed the command 3559 self.internal.cmdsIgnored = false; 3560 } 3561 self._getAuroraStatus(player); 3562 self._updateInterface(); 3563 self._trigger($.jPlayer.event.progress); 3564 // Progress with song duration, we estimate timeupdate need to be triggered too. 3565 if (player.duration > 0) { 3566 self._trigger($.jPlayer.event.timeupdate); 3567 } 3568 } 3569 }, false); 3570 player.on("ready", function() { 3571 if(entity.gate) { 3572 self._trigger($.jPlayer.event.loadeddata); 3573 } 3574 }, false); 3575 player.on("duration", function() { 3576 if(entity.gate) { 3577 self._getAuroraStatus(player); 3578 self._updateInterface(); 3579 self._trigger($.jPlayer.event.durationchange); 3580 } 3581 }, false); 3582 player.on("end", function() { 3583 if(entity.gate) { 3584 // Order of the next few commands are important. Change the time and then pause. 3585 self._updateButtons(false); 3586 self._getAuroraStatus(player, true); 3587 self._updateInterface(); 3588 self._trigger($.jPlayer.event.ended); 3589 } 3590 }, false); 3591 player.on("error", function() { 3592 if(entity.gate) { 3593 self._updateButtons(false); 3594 self._seeked(); 3595 if(self.status.srcSet) { // Deals with case of clearMedia() causing an error event. 3596 self.status.waitForLoad = true; // Allows the load operation to try again. 3597 self.status.waitForPlay = true; // Reset since a play was captured. 3598 if(self.status.video && !self.status.nativeVideoControls) { 3599 self.internal.video.jq.css({'width':'0px', 'height':'0px'}); 3600 } 3601 if(self._validString(self.status.media.poster) && !self.status.nativeVideoControls) { 3602 self.internal.poster.jq.show(); 3603 } 3604 if(self.css.jq.videoPlay.length) { 3605 self.css.jq.videoPlay.show(); 3606 } 3607 self._error( { 3608 type: $.jPlayer.error.URL, 3609 context: self.status.src, // this.src shows absolute urls. Want context to show the url given. 3610 message: $.jPlayer.errorMsg.URL, 3611 hint: $.jPlayer.errorHint.URL 3612 }); 3613 } 3614 } 3615 }, false); 3616 }, 3617 _getHtmlStatus: function(media, override) { 3618 var ct = 0, cpa = 0, sp = 0, cpr = 0; 3619 3620 // Fixes the duration bug in iOS, where the durationchange event occurs when media.duration is not always correct. 3621 // Fixes the initial duration bug in BB OS7, where the media.duration is infinity and displays as NaN:NaN due to Date() using inifity. 3622 if(isFinite(media.duration)) { 3623 this.status.duration = media.duration; 3624 } 3625 3626 ct = media.currentTime; 3627 cpa = (this.status.duration > 0) ? 100 * ct / this.status.duration : 0; 3628 if((typeof media.seekable === "object") && (media.seekable.length > 0)) { 3629 sp = (this.status.duration > 0) ? 100 * media.seekable.end(media.seekable.length-1) / this.status.duration : 100; 3630 cpr = (this.status.duration > 0) ? 100 * media.currentTime / media.seekable.end(media.seekable.length-1) : 0; // Duration conditional for iOS duration bug. ie., seekable.end is a NaN in that case. 3631 } else { 3632 sp = 100; 3633 cpr = cpa; 3634 } 3635 3636 if(override) { 3637 ct = 0; 3638 cpr = 0; 3639 cpa = 0; 3640 } 3641 3642 this.status.seekPercent = sp; 3643 this.status.currentPercentRelative = cpr; 3644 this.status.currentPercentAbsolute = cpa; 3645 this.status.currentTime = ct; 3646 3647 this.status.remaining = this.status.duration - this.status.currentTime; 3648 3649 this.status.videoWidth = media.videoWidth; 3650 this.status.videoHeight = media.videoHeight; 3651 3652 this.status.readyState = media.readyState; 3653 this.status.networkState = media.networkState; 3654 this.status.playbackRate = media.playbackRate; 3655 this.status.ended = media.ended; 3656 }, 3657 _getAuroraStatus: function(player, override) { 3658 var ct = 0, cpa = 0, sp = 0, cpr = 0; 3659 3660 this.status.duration = player.duration / 1000; 3661 3662 ct = player.currentTime / 1000; 3663 cpa = (this.status.duration > 0) ? 100 * ct / this.status.duration : 0; 3664 if(player.buffered > 0) { 3665 sp = (this.status.duration > 0) ? (player.buffered * this.status.duration) / this.status.duration : 100; 3666 cpr = (this.status.duration > 0) ? ct / (player.buffered * this.status.duration) : 0; 3667 } else { 3668 sp = 100; 3669 cpr = cpa; 3670 } 3671 3672 if(override) { 3673 ct = 0; 3674 cpr = 0; 3675 cpa = 0; 3676 } 3677 3678 this.status.seekPercent = sp; 3679 this.status.currentPercentRelative = cpr; 3680 this.status.currentPercentAbsolute = cpa; 3681 this.status.currentTime = ct; 3682 3683 this.status.remaining = this.status.duration - this.status.currentTime; 3684 3685 this.status.readyState = 4; // status.readyState; 3686 this.status.networkState = 0; // status.networkState; 3687 this.status.playbackRate = 1; // status.playbackRate; 3688 this.status.ended = false; // status.ended; 3689 }, 3690 _resetStatus: function() { 3691 this.status = $.extend({}, this.status, $.jPlayer.prototype.status); // Maintains the status properties that persist through a reset. 3692 }, 3693 _trigger: function(eventType, error, warning) { // eventType always valid as called using $.jPlayer.event.eventType 3694 var event = $.Event(eventType); 3695 event.jPlayer = {}; 3696 event.jPlayer.version = $.extend({}, this.version); 3697 event.jPlayer.options = $.extend(true, {}, this.options); // Deep copy 3698 event.jPlayer.status = $.extend(true, {}, this.status); // Deep copy 3699 event.jPlayer.html = $.extend(true, {}, this.html); // Deep copy 3700 event.jPlayer.aurora = $.extend(true, {}, this.aurora); // Deep copy 3701 event.jPlayer.flash = $.extend(true, {}, this.flash); // Deep copy 3702 if(error) { 3703 event.jPlayer.error = $.extend({}, error); 3704 } 3705 if(warning) { 3706 event.jPlayer.warning = $.extend({}, warning); 3707 } 3708 this.element.trigger(event); 3709 }, 3710 jPlayerFlashEvent: function(eventType, status) { // Called from Flash 3711 if(eventType === $.jPlayer.event.ready) { 3712 if(!this.internal.ready) { 3713 this.internal.ready = true; 3714 this.internal.flash.jq.css({'width':'0px', 'height':'0px'}); // Once Flash generates the ready event, minimise to zero as it is not affected by wmode anymore. 3715 3716 this.version.flash = status.version; 3717 if(this.version.needFlash !== this.version.flash) { 3718 this._error( { 3719 type: $.jPlayer.error.VERSION, 3720 context: this.version.flash, 3721 message: $.jPlayer.errorMsg.VERSION + this.version.flash, 3722 hint: $.jPlayer.errorHint.VERSION 3723 }); 3724 } 3725 this._trigger($.jPlayer.event.repeat); // Trigger the repeat event so its handler can initialize itself with the loop option. 3726 this._trigger(eventType); 3727 } else { 3728 // This condition occurs if the Flash is hidden and then shown again. 3729 // Firefox also reloads the Flash if the CSS position changes. position:fixed is used for full screen. 3730 3731 // Only do this if the Flash is the solution being used at the moment. Affects Media players where both solution may be being used. 3732 if(this.flash.gate) { 3733 3734 // Send the current status to the Flash now that it is ready (available) again. 3735 if(this.status.srcSet) { 3736 3737 // Need to read original status before issuing the setMedia command. 3738 var currentTime = this.status.currentTime, 3739 paused = this.status.paused; 3740 3741 this.setMedia(this.status.media); 3742 this.volumeWorker(this.options.volume); 3743 if(currentTime > 0) { 3744 if(paused) { 3745 this.pause(currentTime); 3746 } else { 3747 this.play(currentTime); 3748 } 3749 } 3750 } 3751 this._trigger($.jPlayer.event.flashreset); 3752 } 3753 } 3754 } 3755 if(this.flash.gate) { 3756 switch(eventType) { 3757 case $.jPlayer.event.progress: 3758 this._getFlashStatus(status); 3759 this._updateInterface(); 3760 this._trigger(eventType); 3761 break; 3762 case $.jPlayer.event.timeupdate: 3763 this._getFlashStatus(status); 3764 this._updateInterface(); 3765 this._trigger(eventType); 3766 break; 3767 case $.jPlayer.event.play: 3768 this._seeked(); 3769 this._updateButtons(true); 3770 this._trigger(eventType); 3771 break; 3772 case $.jPlayer.event.pause: 3773 this._updateButtons(false); 3774 this._trigger(eventType); 3775 break; 3776 case $.jPlayer.event.ended: 3777 this._updateButtons(false); 3778 this._trigger(eventType); 3779 break; 3780 case $.jPlayer.event.click: 3781 this._trigger(eventType); // This could be dealt with by the default 3782 break; 3783 case $.jPlayer.event.error: 3784 this.status.waitForLoad = true; // Allows the load operation to try again. 3785 this.status.waitForPlay = true; // Reset since a play was captured. 3786 if(this.status.video) { 3787 this.internal.flash.jq.css({'width':'0px', 'height':'0px'}); 3788 } 3789 if(this._validString(this.status.media.poster)) { 3790 this.internal.poster.jq.show(); 3791 } 3792 if(this.css.jq.videoPlay.length && this.status.video) { 3793 this.css.jq.videoPlay.show(); 3794 } 3795 if(this.status.video) { // Set up for another try. Execute before error event. 3796 this._flash_setVideo(this.status.media); 3797 } else { 3798 this._flash_setAudio(this.status.media); 3799 } 3800 this._updateButtons(false); 3801 this._error( { 3802 type: $.jPlayer.error.URL, 3803 context:status.src, 3804 message: $.jPlayer.errorMsg.URL, 3805 hint: $.jPlayer.errorHint.URL 3806 }); 3807 break; 3808 case $.jPlayer.event.seeking: 3809 this._seeking(); 3810 this._trigger(eventType); 3811 break; 3812 case $.jPlayer.event.seeked: 3813 this._seeked(); 3814 this._trigger(eventType); 3815 break; 3816 case $.jPlayer.event.ready: 3817 // The ready event is handled outside the switch statement. 3818 // Captured here otherwise 2 ready events would be generated if the ready event handler used setMedia. 3819 break; 3820 default: 3821 this._trigger(eventType); 3822 } 3823 } 3824 return false; 3825 }, 3826 _getFlashStatus: function(status) { 3827 this.status.seekPercent = status.seekPercent; 3828 this.status.currentPercentRelative = status.currentPercentRelative; 3829 this.status.currentPercentAbsolute = status.currentPercentAbsolute; 3830 this.status.currentTime = status.currentTime; 3831 this.status.duration = status.duration; 3832 this.status.remaining = status.duration - status.currentTime; 3833 3834 this.status.videoWidth = status.videoWidth; 3835 this.status.videoHeight = status.videoHeight; 3836 3837 // The Flash does not generate this information in this release 3838 this.status.readyState = 4; // status.readyState; 3839 this.status.networkState = 0; // status.networkState; 3840 this.status.playbackRate = 1; // status.playbackRate; 3841 this.status.ended = false; // status.ended; 3842 }, 3843 _updateButtons: function(playing) { 3844 if(playing === undefined) { 3845 playing = !this.status.paused; 3846 } else { 3847 this.status.paused = !playing; 3848 } 3849 // Apply the state classes. (For the useStateClassSkin:true option) 3850 if(playing) { 3851 this.addStateClass('playing'); 3852 } else { 3853 this.removeStateClass('playing'); 3854 } 3855 if(!this.status.noFullWindow && this.options.fullWindow) { 3856 this.addStateClass('fullScreen'); 3857 } else { 3858 this.removeStateClass('fullScreen'); 3859 } 3860 if(this.options.loop) { 3861 this.addStateClass('looped'); 3862 } else { 3863 this.removeStateClass('looped'); 3864 } 3865 // Toggle the GUI element pairs. (For the useStateClassSkin:false option) 3866 if(this.css.jq.play.length && this.css.jq.pause.length) { 3867 if(playing) { 3868 this.css.jq.play.hide(); 3869 this.css.jq.pause.show(); 3870 } else { 3871 this.css.jq.play.show(); 3872 this.css.jq.pause.hide(); 3873 } 3874 } 3875 if(this.css.jq.restoreScreen.length && this.css.jq.fullScreen.length) { 3876 if(this.status.noFullWindow) { 3877 this.css.jq.fullScreen.hide(); 3878 this.css.jq.restoreScreen.hide(); 3879 } else if(this.options.fullWindow) { 3880 this.css.jq.fullScreen.hide(); 3881 this.css.jq.restoreScreen.show(); 3882 } else { 3883 this.css.jq.fullScreen.show(); 3884 this.css.jq.restoreScreen.hide(); 3885 } 3886 } 3887 if(this.css.jq.repeat.length && this.css.jq.repeatOff.length) { 3888 if(this.options.loop) { 3889 this.css.jq.repeat.hide(); 3890 this.css.jq.repeatOff.show(); 3891 } else { 3892 this.css.jq.repeat.show(); 3893 this.css.jq.repeatOff.hide(); 3894 } 3895 } 3896 }, 3897 _updateInterface: function() { 3898 if(this.css.jq.seekBar.length) { 3899 this.css.jq.seekBar.width(this.status.seekPercent+"%"); 3900 } 3901 if(this.css.jq.playBar.length) { 3902 if(this.options.smoothPlayBar) { 3903 this.css.jq.playBar.stop().animate({ 3904 width: this.status.currentPercentAbsolute+"%" 3905 }, 250, "linear"); 3906 } else { 3907 this.css.jq.playBar.width(this.status.currentPercentRelative+"%"); 3908 } 3909 } 3910 var currentTimeText = ''; 3911 if(this.css.jq.currentTime.length) { 3912 currentTimeText = this._convertTime(this.status.currentTime); 3913 if(currentTimeText !== this.css.jq.currentTime.text()) { 3914 this.css.jq.currentTime.text(this._convertTime(this.status.currentTime)); 3915 } 3916 } 3917 var durationText = '', 3918 duration = this.status.duration, 3919 remaining = this.status.remaining; 3920 if(this.css.jq.duration.length) { 3921 if(typeof this.status.media.duration === 'string') { 3922 durationText = this.status.media.duration; 3923 } else { 3924 if(typeof this.status.media.duration === 'number') { 3925 duration = this.status.media.duration; 3926 remaining = duration - this.status.currentTime; 3927 } 3928 if(this.options.remainingDuration) { 3929 durationText = (remaining > 0 ? '-' : '') + this._convertTime(remaining); 3930 } else { 3931 durationText = this._convertTime(duration); 3932 } 3933 } 3934 if(durationText !== this.css.jq.duration.text()) { 3935 this.css.jq.duration.text(durationText); 3936 } 3937 } 3938 }, 3939 _convertTime: ConvertTime.prototype.time, 3940 _seeking: function() { 3941 if(this.css.jq.seekBar.length) { 3942 this.css.jq.seekBar.addClass("jp-seeking-bg"); 3943 } 3944 this.addStateClass('seeking'); 3945 }, 3946 _seeked: function() { 3947 if(this.css.jq.seekBar.length) { 3948 this.css.jq.seekBar.removeClass("jp-seeking-bg"); 3949 } 3950 this.removeStateClass('seeking'); 3951 }, 3952 _resetGate: function() { 3953 this.html.audio.gate = false; 3954 this.html.video.gate = false; 3955 this.aurora.gate = false; 3956 this.flash.gate = false; 3957 }, 3958 _resetActive: function() { 3959 this.html.active = false; 3960 this.aurora.active = false; 3961 this.flash.active = false; 3962 }, 3963 _escapeHtml: function(s) { 3964 return s.split('&').join('&').split('<').join('<').split('>').join('>').split('"').join('"'); 3965 }, 3966 _qualifyURL: function(url) { 3967 var el = document.createElement('div'); 3968 el.innerHTML= '<a href="' + this._escapeHtml(url) + '">x</a>'; 3969 return el.firstChild.href; 3970 }, 3971 _absoluteMediaUrls: function(media) { 3972 var self = this; 3973 $.each(media, function(type, url) { 3974 if(url && self.format[type] && url.substr(0, 5) !== "data:") { 3975 media[type] = self._qualifyURL(url); 3976 } 3977 }); 3978 return media; 3979 }, 3980 addStateClass: function(state) { 3981 if(this.ancestorJq.length) { 3982 this.ancestorJq.addClass(this.options.stateClass[state]); 3983 } 3984 }, 3985 removeStateClass: function(state) { 3986 if(this.ancestorJq.length) { 3987 this.ancestorJq.removeClass(this.options.stateClass[state]); 3988 } 3989 }, 3990 setMedia: function(media) { 3991 3992 /* media[format] = String: URL of format. Must contain all of the supplied option's video or audio formats. 3993 * media.poster = String: Video poster URL. 3994 * media.track = Array: Of objects defining the track element: kind, src, srclang, label, def. 3995 * media.stream = Boolean: * NOT IMPLEMENTED * Designating actual media streams. ie., "false/undefined" for files. Plan to refresh the flash every so often. 3996 */ 3997 3998 var self = this, 3999 supported = false, 4000 posterChanged = this.status.media.poster !== media.poster; // Compare before reset. Important for OSX Safari as this.htmlElement.poster.src is absolute, even if original poster URL was relative. 4001 4002 this._resetMedia(); 4003 this._resetGate(); 4004 this._resetActive(); 4005 4006 // Clear the Android Fix. 4007 this.androidFix.setMedia = false; 4008 this.androidFix.play = false; 4009 this.androidFix.pause = false; 4010 4011 // Convert all media URLs to absolute URLs. 4012 media = this._absoluteMediaUrls(media); 4013 4014 $.each(this.formats, function(formatPriority, format) { 4015 var isVideo = self.format[format].media === 'video'; 4016 $.each(self.solutions, function(solutionPriority, solution) { 4017 if(self[solution].support[format] && self._validString(media[format])) { // Format supported in solution and url given for format. 4018 var isHtml = solution === 'html'; 4019 var isAurora = solution === 'aurora'; 4020 4021 if(isVideo) { 4022 if(isHtml) { 4023 self.html.video.gate = true; 4024 self._html_setVideo(media); 4025 self.html.active = true; 4026 } else { 4027 self.flash.gate = true; 4028 self._flash_setVideo(media); 4029 self.flash.active = true; 4030 } 4031 if(self.css.jq.videoPlay.length) { 4032 self.css.jq.videoPlay.show(); 4033 } 4034 self.status.video = true; 4035 } else { 4036 if(isHtml) { 4037 self.html.audio.gate = true; 4038 self._html_setAudio(media); 4039 self.html.active = true; 4040 4041 // Setup the Android Fix - Only for HTML audio. 4042 if($.jPlayer.platform.android) { 4043 self.androidFix.setMedia = true; 4044 } 4045 } else if(isAurora) { 4046 self.aurora.gate = true; 4047 self._aurora_setAudio(media); 4048 self.aurora.active = true; 4049 } else { 4050 self.flash.gate = true; 4051 self._flash_setAudio(media); 4052 self.flash.active = true; 4053 } 4054 if(self.css.jq.videoPlay.length) { 4055 self.css.jq.videoPlay.hide(); 4056 } 4057 self.status.video = false; 4058 } 4059 4060 supported = true; 4061 return false; // Exit $.each 4062 } 4063 }); 4064 if(supported) { 4065 return false; // Exit $.each 4066 } 4067 }); 4068 4069 if(supported) { 4070 if(!(this.status.nativeVideoControls && this.html.video.gate)) { 4071 // Set poster IMG if native video controls are not being used 4072 // Note: With IE the IMG onload event occurs immediately when cached. 4073 // Note: Poster hidden by default in _resetMedia() 4074 if(this._validString(media.poster)) { 4075 if(posterChanged) { // Since some browsers do not generate img onload event. 4076 this.htmlElement.poster.src = media.poster; 4077 } else { 4078 this.internal.poster.jq.show(); 4079 } 4080 } 4081 } 4082 if(typeof media.title === 'string') { 4083 if(this.css.jq.title.length) { 4084 this.css.jq.title.html(media.title); 4085 } 4086 if(this.htmlElement.audio) { 4087 this.htmlElement.audio.setAttribute('title', media.title); 4088 } 4089 if(this.htmlElement.video) { 4090 this.htmlElement.video.setAttribute('title', media.title); 4091 } 4092 } 4093 this.status.srcSet = true; 4094 this.status.media = $.extend({}, media); 4095 this._updateButtons(false); 4096 this._updateInterface(); 4097 this._trigger($.jPlayer.event.setmedia); 4098 } else { // jPlayer cannot support any formats provided in this browser 4099 // Send an error event 4100 this._error( { 4101 type: $.jPlayer.error.NO_SUPPORT, 4102 context: "{supplied:'" + this.options.supplied + "'}", 4103 message: $.jPlayer.errorMsg.NO_SUPPORT, 4104 hint: $.jPlayer.errorHint.NO_SUPPORT 4105 }); 4106 } 4107 }, 4108 _resetMedia: function() { 4109 this._resetStatus(); 4110 this._updateButtons(false); 4111 this._updateInterface(); 4112 this._seeked(); 4113 this.internal.poster.jq.hide(); 4114 4115 clearTimeout(this.internal.htmlDlyCmdId); 4116 4117 if(this.html.active) { 4118 this._html_resetMedia(); 4119 } else if(this.aurora.active) { 4120 this._aurora_resetMedia(); 4121 } else if(this.flash.active) { 4122 this._flash_resetMedia(); 4123 } 4124 }, 4125 clearMedia: function() { 4126 this._resetMedia(); 4127 4128 if(this.html.active) { 4129 this._html_clearMedia(); 4130 } else if(this.aurora.active) { 4131 this._aurora_clearMedia(); 4132 } else if(this.flash.active) { 4133 this._flash_clearMedia(); 4134 } 4135 4136 this._resetGate(); 4137 this._resetActive(); 4138 }, 4139 load: function() { 4140 if(this.status.srcSet) { 4141 if(this.html.active) { 4142 this._html_load(); 4143 } else if(this.aurora.active) { 4144 this._aurora_load(); 4145 } else if(this.flash.active) { 4146 this._flash_load(); 4147 } 4148 } else { 4149 this._urlNotSetError("load"); 4150 } 4151 }, 4152 focus: function() { 4153 if(this.options.keyEnabled) { 4154 $.jPlayer.focus = this; 4155 } 4156 }, 4157 play: function(time) { 4158 var guiAction = typeof time === "object"; // Flags GUI click events so we know this was not a direct command, but an action taken by the user on the GUI. 4159 if(guiAction && this.options.useStateClassSkin && !this.status.paused) { 4160 this.pause(time); // The time would be the click event, but passing it over so info is not lost. 4161 } else { 4162 time = (typeof time === "number") ? time : NaN; // Remove jQuery event from click handler 4163 if(this.status.srcSet) { 4164 this.focus(); 4165 if(this.html.active) { 4166 this._html_play(time); 4167 } else if(this.aurora.active) { 4168 this._aurora_play(time); 4169 } else if(this.flash.active) { 4170 this._flash_play(time); 4171 } 4172 } else { 4173 this._urlNotSetError("play"); 4174 } 4175 } 4176 }, 4177 videoPlay: function() { // Handles clicks on the play button over the video poster 4178 this.play(); 4179 }, 4180 pause: function(time) { 4181 time = (typeof time === "number") ? time : NaN; // Remove jQuery event from click handler 4182 if(this.status.srcSet) { 4183 if(this.html.active) { 4184 this._html_pause(time); 4185 } else if(this.aurora.active) { 4186 this._aurora_pause(time); 4187 } else if(this.flash.active) { 4188 this._flash_pause(time); 4189 } 4190 } else { 4191 this._urlNotSetError("pause"); 4192 } 4193 }, 4194 tellOthers: function(command, conditions) { 4195 var self = this, 4196 hasConditions = typeof conditions === 'function', 4197 args = Array.prototype.slice.call(arguments); // Convert arguments to an Array. 4198 4199 if(typeof command !== 'string') { // Ignore, since no command. 4200 return; // Return undefined to maintain chaining. 4201 } 4202 if(hasConditions) { 4203 args.splice(1, 1); // Remove the conditions from the arguments 4204 } 4205 4206 $.jPlayer.prototype.destroyRemoved(); 4207 $.each(this.instances, function() { 4208 // Remember that "this" is the instance's "element" in the $.each() loop. 4209 if(self.element !== this) { // Do not tell my instance. 4210 if(!hasConditions || conditions.call(this.data("jPlayer"), self)) { 4211 this.jPlayer.apply(this, args); 4212 } 4213 } 4214 }); 4215 }, 4216 pauseOthers: function(time) { 4217 this.tellOthers("pause", function() { 4218 // In the conditions function, the "this" context is the other instance's jPlayer object. 4219 return this.status.srcSet; 4220 }, time); 4221 }, 4222 stop: function() { 4223 if(this.status.srcSet) { 4224 if(this.html.active) { 4225 this._html_pause(0); 4226 } else if(this.aurora.active) { 4227 this._aurora_pause(0); 4228 } else if(this.flash.active) { 4229 this._flash_pause(0); 4230 } 4231 } else { 4232 this._urlNotSetError("stop"); 4233 } 4234 }, 4235 playHead: function(p) { 4236 p = this._limitValue(p, 0, 100); 4237 if(this.status.srcSet) { 4238 if(this.html.active) { 4239 this._html_playHead(p); 4240 } else if(this.aurora.active) { 4241 this._aurora_playHead(p); 4242 } else if(this.flash.active) { 4243 this._flash_playHead(p); 4244 } 4245 } else { 4246 this._urlNotSetError("playHead"); 4247 } 4248 }, 4249 _muted: function(muted) { 4250 this.mutedWorker(muted); 4251 if(this.options.globalVolume) { 4252 this.tellOthers("mutedWorker", function() { 4253 // Check the other instance has global volume enabled. 4254 return this.options.globalVolume; 4255 }, muted); 4256 } 4257 }, 4258 mutedWorker: function(muted) { 4259 this.options.muted = muted; 4260 if(this.html.used) { 4261 this._html_setProperty('muted', muted); 4262 } 4263 if(this.aurora.used) { 4264 this._aurora_mute(muted); 4265 } 4266 if(this.flash.used) { 4267 this._flash_mute(muted); 4268 } 4269 4270 // The HTML solution generates this event from the media element itself. 4271 if(!this.html.video.gate && !this.html.audio.gate) { 4272 this._updateMute(muted); 4273 this._updateVolume(this.options.volume); 4274 this._trigger($.jPlayer.event.volumechange); 4275 } 4276 }, 4277 mute: function(mute) { // mute is either: undefined (true), an event object (true) or a boolean (muted). 4278 var guiAction = typeof mute === "object"; // Flags GUI click events so we know this was not a direct command, but an action taken by the user on the GUI. 4279 if(guiAction && this.options.useStateClassSkin && this.options.muted) { 4280 this._muted(false); 4281 } else { 4282 mute = mute === undefined ? true : !!mute; 4283 this._muted(mute); 4284 } 4285 }, 4286 unmute: function(unmute) { // unmute is either: undefined (true), an event object (true) or a boolean (!muted). 4287 unmute = unmute === undefined ? true : !!unmute; 4288 this._muted(!unmute); 4289 }, 4290 _updateMute: function(mute) { 4291 if(mute === undefined) { 4292 mute = this.options.muted; 4293 } 4294 if(mute) { 4295 this.addStateClass('muted'); 4296 } else { 4297 this.removeStateClass('muted'); 4298 } 4299 if(this.css.jq.mute.length && this.css.jq.unmute.length) { 4300 if(this.status.noVolume) { 4301 this.css.jq.mute.hide(); 4302 this.css.jq.unmute.hide(); 4303 } else if(mute) { 4304 this.css.jq.mute.hide(); 4305 this.css.jq.unmute.show(); 4306 } else { 4307 this.css.jq.mute.show(); 4308 this.css.jq.unmute.hide(); 4309 } 4310 } 4311 }, 4312 volume: function(v) { 4313 this.volumeWorker(v); 4314 if(this.options.globalVolume) { 4315 this.tellOthers("volumeWorker", function() { 4316 // Check the other instance has global volume enabled. 4317 return this.options.globalVolume; 4318 }, v); 4319 } 4320 }, 4321 volumeWorker: function(v) { 4322 v = this._limitValue(v, 0, 1); 4323 this.options.volume = v; 4324 4325 if(this.html.used) { 4326 this._html_setProperty('volume', v); 4327 } 4328 if(this.aurora.used) { 4329 this._aurora_volume(v); 4330 } 4331 if(this.flash.used) { 4332 this._flash_volume(v); 4333 } 4334 4335 // The HTML solution generates this event from the media element itself. 4336 if(!this.html.video.gate && !this.html.audio.gate) { 4337 this._updateVolume(v); 4338 this._trigger($.jPlayer.event.volumechange); 4339 } 4340 }, 4341 volumeBar: function(e) { // Handles clicks on the volumeBar 4342 if(this.css.jq.volumeBar.length) { 4343 // Using $(e.currentTarget) to enable multiple volume bars 4344 var $bar = $(e.currentTarget), 4345 offset = $bar.offset(), 4346 x = e.pageX - offset.left, 4347 w = $bar.width(), 4348 y = $bar.height() - e.pageY + offset.top, 4349 h = $bar.height(); 4350 if(this.options.verticalVolume) { 4351 this.volume(y/h); 4352 } else { 4353 this.volume(x/w); 4354 } 4355 } 4356 if(this.options.muted) { 4357 this._muted(false); 4358 } 4359 }, 4360 _updateVolume: function(v) { 4361 if(v === undefined) { 4362 v = this.options.volume; 4363 } 4364 v = this.options.muted ? 0 : v; 4365 4366 if(this.status.noVolume) { 4367 this.addStateClass('noVolume'); 4368 if(this.css.jq.volumeBar.length) { 4369 this.css.jq.volumeBar.hide(); 4370 } 4371 if(this.css.jq.volumeBarValue.length) { 4372 this.css.jq.volumeBarValue.hide(); 4373 } 4374 if(this.css.jq.volumeMax.length) { 4375 this.css.jq.volumeMax.hide(); 4376 } 4377 } else { 4378 this.removeStateClass('noVolume'); 4379 if(this.css.jq.volumeBar.length) { 4380 this.css.jq.volumeBar.show(); 4381 } 4382 if(this.css.jq.volumeBarValue.length) { 4383 this.css.jq.volumeBarValue.show(); 4384 this.css.jq.volumeBarValue[this.options.verticalVolume ? "height" : "width"]((v*100)+"%"); 4385 } 4386 if(this.css.jq.volumeMax.length) { 4387 this.css.jq.volumeMax.show(); 4388 } 4389 } 4390 }, 4391 volumeMax: function() { // Handles clicks on the volume max 4392 this.volume(1); 4393 if(this.options.muted) { 4394 this._muted(false); 4395 } 4396 }, 4397 _cssSelectorAncestor: function(ancestor) { 4398 var self = this; 4399 this.options.cssSelectorAncestor = ancestor; 4400 this._removeUiClass(); 4401 this.ancestorJq = ancestor ? $(ancestor) : []; // Would use $() instead of [], but it is only 1.4+ 4402 if(ancestor && this.ancestorJq.length !== 1) { // So empty strings do not generate the warning. 4403 this._warning( { 4404 type: $.jPlayer.warning.CSS_SELECTOR_COUNT, 4405 context: ancestor, 4406 message: $.jPlayer.warningMsg.CSS_SELECTOR_COUNT + this.ancestorJq.length + " found for cssSelectorAncestor.", 4407 hint: $.jPlayer.warningHint.CSS_SELECTOR_COUNT 4408 }); 4409 } 4410 this._addUiClass(); 4411 $.each(this.options.cssSelector, function(fn, cssSel) { 4412 self._cssSelector(fn, cssSel); 4413 }); 4414 4415 // Set the GUI to the current state. 4416 this._updateInterface(); 4417 this._updateButtons(); 4418 this._updateAutohide(); 4419 this._updateVolume(); 4420 this._updateMute(); 4421 }, 4422 _cssSelector: function(fn, cssSel) { 4423 var self = this; 4424 if(typeof cssSel === 'string') { 4425 if($.jPlayer.prototype.options.cssSelector[fn]) { 4426 if(this.css.jq[fn] && this.css.jq[fn].length) { 4427 this.css.jq[fn].unbind(".jPlayer"); 4428 } 4429 this.options.cssSelector[fn] = cssSel; 4430 this.css.cs[fn] = this.options.cssSelectorAncestor + " " + cssSel; 4431 4432 if(cssSel) { // Checks for empty string 4433 this.css.jq[fn] = $(this.css.cs[fn]); 4434 } else { 4435 this.css.jq[fn] = []; // To comply with the css.jq[fn].length check before its use. As of jQuery 1.4 could have used $() for an empty set. 4436 } 4437 4438 if(this.css.jq[fn].length && this[fn]) { 4439 var handler = function(e) { 4440 e.preventDefault(); 4441 self[fn](e); 4442 if(self.options.autoBlur) { 4443 $(this).blur(); 4444 } else { 4445 $(this).focus(); // Force focus for ARIA. 4446 } 4447 }; 4448 this.css.jq[fn].bind("click.jPlayer", handler); // Using jPlayer namespace 4449 } 4450 4451 if(cssSel && this.css.jq[fn].length !== 1) { // So empty strings do not generate the warning. ie., they just remove the old one. 4452 this._warning( { 4453 type: $.jPlayer.warning.CSS_SELECTOR_COUNT, 4454 context: this.css.cs[fn], 4455 message: $.jPlayer.warningMsg.CSS_SELECTOR_COUNT + this.css.jq[fn].length + " found for " + fn + " method.", 4456 hint: $.jPlayer.warningHint.CSS_SELECTOR_COUNT 4457 }); 4458 } 4459 } else { 4460 this._warning( { 4461 type: $.jPlayer.warning.CSS_SELECTOR_METHOD, 4462 context: fn, 4463 message: $.jPlayer.warningMsg.CSS_SELECTOR_METHOD, 4464 hint: $.jPlayer.warningHint.CSS_SELECTOR_METHOD 4465 }); 4466 } 4467 } else { 4468 this._warning( { 4469 type: $.jPlayer.warning.CSS_SELECTOR_STRING, 4470 context: cssSel, 4471 message: $.jPlayer.warningMsg.CSS_SELECTOR_STRING, 4472 hint: $.jPlayer.warningHint.CSS_SELECTOR_STRING 4473 }); 4474 } 4475 }, 4476 duration: function(e) { 4477 if(this.options.toggleDuration) { 4478 if(this.options.captureDuration) { 4479 e.stopPropagation(); 4480 } 4481 this._setOption("remainingDuration", !this.options.remainingDuration); 4482 } 4483 }, 4484 seekBar: function(e) { // Handles clicks on the seekBar 4485 if(this.css.jq.seekBar.length) { 4486 // Using $(e.currentTarget) to enable multiple seek bars 4487 var $bar = $(e.currentTarget), 4488 offset = $bar.offset(), 4489 x = e.pageX - offset.left, 4490 w = $bar.width(), 4491 p = 100 * x / w; 4492 this.playHead(p); 4493 } 4494 }, 4495 playbackRate: function(pbr) { 4496 this._setOption("playbackRate", pbr); 4497 }, 4498 playbackRateBar: function(e) { // Handles clicks on the playbackRateBar 4499 if(this.css.jq.playbackRateBar.length) { 4500 // Using $(e.currentTarget) to enable multiple playbackRate bars 4501 var $bar = $(e.currentTarget), 4502 offset = $bar.offset(), 4503 x = e.pageX - offset.left, 4504 w = $bar.width(), 4505 y = $bar.height() - e.pageY + offset.top, 4506 h = $bar.height(), 4507 ratio, pbr; 4508 if(this.options.verticalPlaybackRate) { 4509 ratio = y/h; 4510 } else { 4511 ratio = x/w; 4512 } 4513 pbr = ratio * (this.options.maxPlaybackRate - this.options.minPlaybackRate) + this.options.minPlaybackRate; 4514 this.playbackRate(pbr); 4515 } 4516 }, 4517 _updatePlaybackRate: function() { 4518 var pbr = this.options.playbackRate, 4519 ratio = (pbr - this.options.minPlaybackRate) / (this.options.maxPlaybackRate - this.options.minPlaybackRate); 4520 if(this.status.playbackRateEnabled) { 4521 if(this.css.jq.playbackRateBar.length) { 4522 this.css.jq.playbackRateBar.show(); 4523 } 4524 if(this.css.jq.playbackRateBarValue.length) { 4525 this.css.jq.playbackRateBarValue.show(); 4526 this.css.jq.playbackRateBarValue[this.options.verticalPlaybackRate ? "height" : "width"]((ratio*100)+"%"); 4527 } 4528 } else { 4529 if(this.css.jq.playbackRateBar.length) { 4530 this.css.jq.playbackRateBar.hide(); 4531 } 4532 if(this.css.jq.playbackRateBarValue.length) { 4533 this.css.jq.playbackRateBarValue.hide(); 4534 } 4535 } 4536 }, 4537 repeat: function(event) { // Handle clicks on the repeat button 4538 var guiAction = typeof event === "object"; // Flags GUI click events so we know this was not a direct command, but an action taken by the user on the GUI. 4539 if(guiAction && this.options.useStateClassSkin && this.options.loop) { 4540 this._loop(false); 4541 } else { 4542 this._loop(true); 4543 } 4544 }, 4545 repeatOff: function() { // Handle clicks on the repeatOff button 4546 this._loop(false); 4547 }, 4548 _loop: function(loop) { 4549 if(this.options.loop !== loop) { 4550 this.options.loop = loop; 4551 this._updateButtons(); 4552 this._trigger($.jPlayer.event.repeat); 4553 } 4554 }, 4555 4556 // Options code adapted from ui.widget.js (1.8.7). Made changes so the key can use dot notation. To match previous getData solution in jPlayer 1. 4557 option: function(key, value) { 4558 var options = key; 4559 4560 // Enables use: options(). Returns a copy of options object 4561 if ( arguments.length === 0 ) { 4562 return $.extend( true, {}, this.options ); 4563 } 4564 4565 if(typeof key === "string") { 4566 var keys = key.split("."); 4567 4568 // Enables use: options("someOption") Returns a copy of the option. Supports dot notation. 4569 if(value === undefined) { 4570 4571 var opt = $.extend(true, {}, this.options); 4572 for(var i = 0; i < keys.length; i++) { 4573 if(opt[keys[i]] !== undefined) { 4574 opt = opt[keys[i]]; 4575 } else { 4576 this._warning( { 4577 type: $.jPlayer.warning.OPTION_KEY, 4578 context: key, 4579 message: $.jPlayer.warningMsg.OPTION_KEY, 4580 hint: $.jPlayer.warningHint.OPTION_KEY 4581 }); 4582 return undefined; 4583 } 4584 } 4585 return opt; 4586 } 4587 4588 // Enables use: options("someOptionObject", someObject}). Creates: {someOptionObject:someObject} 4589 // Enables use: options("someOption", someValue). Creates: {someOption:someValue} 4590 // Enables use: options("someOptionObject.someOption", someValue). Creates: {someOptionObject:{someOption:someValue}} 4591 4592 options = {}; 4593 var opts = options; 4594 4595 for(var j = 0; j < keys.length; j++) { 4596 if(j < keys.length - 1) { 4597 opts[keys[j]] = {}; 4598 opts = opts[keys[j]]; 4599 } else { 4600 opts[keys[j]] = value; 4601 } 4602 } 4603 } 4604 4605 // Otherwise enables use: options(optionObject). Uses original object (the key) 4606 4607 this._setOptions(options); 4608 4609 return this; 4610 }, 4611 _setOptions: function(options) { 4612 var self = this; 4613 $.each(options, function(key, value) { // This supports the 2 level depth that the options of jPlayer has. Would review if we ever need more depth. 4614 self._setOption(key, value); 4615 }); 4616 4617 return this; 4618 }, 4619 _setOption: function(key, value) { 4620 var self = this; 4621 4622 // The ability to set options is limited at this time. 4623 4624 switch(key) { 4625 case "volume" : 4626 this.volume(value); 4627 break; 4628 case "muted" : 4629 this._muted(value); 4630 break; 4631 case "globalVolume" : 4632 this.options[key] = value; 4633 break; 4634 case "cssSelectorAncestor" : 4635 this._cssSelectorAncestor(value); // Set and refresh all associations for the new ancestor. 4636 break; 4637 case "cssSelector" : 4638 $.each(value, function(fn, cssSel) { 4639 self._cssSelector(fn, cssSel); // NB: The option is set inside this function, after further validity checks. 4640 }); 4641 break; 4642 case "playbackRate" : 4643 this.options[key] = value = this._limitValue(value, this.options.minPlaybackRate, this.options.maxPlaybackRate); 4644 if(this.html.used) { 4645 this._html_setProperty('playbackRate', value); 4646 } 4647 this._updatePlaybackRate(); 4648 break; 4649 case "defaultPlaybackRate" : 4650 this.options[key] = value = this._limitValue(value, this.options.minPlaybackRate, this.options.maxPlaybackRate); 4651 if(this.html.used) { 4652 this._html_setProperty('defaultPlaybackRate', value); 4653 } 4654 this._updatePlaybackRate(); 4655 break; 4656 case "minPlaybackRate" : 4657 this.options[key] = value = this._limitValue(value, 0.1, this.options.maxPlaybackRate - 0.1); 4658 this._updatePlaybackRate(); 4659 break; 4660 case "maxPlaybackRate" : 4661 this.options[key] = value = this._limitValue(value, this.options.minPlaybackRate + 0.1, 16); 4662 this._updatePlaybackRate(); 4663 break; 4664 case "fullScreen" : 4665 if(this.options[key] !== value) { // if changed 4666 var wkv = $.jPlayer.nativeFeatures.fullscreen.used.webkitVideo; 4667 if(!wkv || wkv && !this.status.waitForPlay) { 4668 if(!wkv) { // No sensible way to unset option on these devices. 4669 this.options[key] = value; 4670 } 4671 if(value) { 4672 this._requestFullscreen(); 4673 } else { 4674 this._exitFullscreen(); 4675 } 4676 if(!wkv) { 4677 this._setOption("fullWindow", value); 4678 } 4679 } 4680 } 4681 break; 4682 case "fullWindow" : 4683 if(this.options[key] !== value) { // if changed 4684 this._removeUiClass(); 4685 this.options[key] = value; 4686 this._refreshSize(); 4687 } 4688 break; 4689 case "size" : 4690 if(!this.options.fullWindow && this.options[key].cssClass !== value.cssClass) { 4691 this._removeUiClass(); 4692 } 4693 this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed. 4694 this._refreshSize(); 4695 break; 4696 case "sizeFull" : 4697 if(this.options.fullWindow && this.options[key].cssClass !== value.cssClass) { 4698 this._removeUiClass(); 4699 } 4700 this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed. 4701 this._refreshSize(); 4702 break; 4703 case "autohide" : 4704 this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed. 4705 this._updateAutohide(); 4706 break; 4707 case "loop" : 4708 this._loop(value); 4709 break; 4710 case "remainingDuration" : 4711 this.options[key] = value; 4712 this._updateInterface(); 4713 break; 4714 case "toggleDuration" : 4715 this.options[key] = value; 4716 break; 4717 case "nativeVideoControls" : 4718 this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed. 4719 this.status.nativeVideoControls = this._uaBlocklist(this.options.nativeVideoControls); 4720 this._restrictNativeVideoControls(); 4721 this._updateNativeVideoControls(); 4722 break; 4723 case "noFullWindow" : 4724 this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed. 4725 this.status.nativeVideoControls = this._uaBlocklist(this.options.nativeVideoControls); // Need to check again as noFullWindow can depend on this flag and the restrict() can override it. 4726 this.status.noFullWindow = this._uaBlocklist(this.options.noFullWindow); 4727 this._restrictNativeVideoControls(); 4728 this._updateButtons(); 4729 break; 4730 case "noVolume" : 4731 this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed. 4732 this.status.noVolume = this._uaBlocklist(this.options.noVolume); 4733 this._updateVolume(); 4734 this._updateMute(); 4735 break; 4736 case "emulateHtml" : 4737 if(this.options[key] !== value) { // To avoid multiple event handlers being created, if true already. 4738 this.options[key] = value; 4739 if(value) { 4740 this._emulateHtmlBridge(); 4741 } else { 4742 this._destroyHtmlBridge(); 4743 } 4744 } 4745 break; 4746 case "timeFormat" : 4747 this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed. 4748 break; 4749 case "keyEnabled" : 4750 this.options[key] = value; 4751 if(!value && this === $.jPlayer.focus) { 4752 $.jPlayer.focus = null; 4753 } 4754 break; 4755 case "keyBindings" : 4756 this.options[key] = $.extend(true, {}, this.options[key], value); // store a merged DEEP copy of it, incase not all properties changed. 4757 break; 4758 case "audioFullScreen" : 4759 this.options[key] = value; 4760 break; 4761 case "autoBlur" : 4762 this.options[key] = value; 4763 break; 4764 } 4765 4766 return this; 4767 }, 4768 // End of: (Options code adapted from ui.widget.js) 4769 4770 _refreshSize: function() { 4771 this._setSize(); // update status and jPlayer element size 4772 this._addUiClass(); // update the ui class 4773 this._updateSize(); // update internal sizes 4774 this._updateButtons(); 4775 this._updateAutohide(); 4776 this._trigger($.jPlayer.event.resize); 4777 }, 4778 _setSize: function() { 4779 // Determine the current size from the options 4780 if(this.options.fullWindow) { 4781 this.status.width = this.options.sizeFull.width; 4782 this.status.height = this.options.sizeFull.height; 4783 this.status.cssClass = this.options.sizeFull.cssClass; 4784 } else { 4785 this.status.width = this.options.size.width; 4786 this.status.height = this.options.size.height; 4787 this.status.cssClass = this.options.size.cssClass; 4788 } 4789 4790 // Set the size of the jPlayer area. 4791 this.element.css({'width': this.status.width, 'height': this.status.height}); 4792 }, 4793 _addUiClass: function() { 4794 if(this.ancestorJq.length) { 4795 this.ancestorJq.addClass(this.status.cssClass); 4796 } 4797 }, 4798 _removeUiClass: function() { 4799 if(this.ancestorJq.length) { 4800 this.ancestorJq.removeClass(this.status.cssClass); 4801 } 4802 }, 4803 _updateSize: function() { 4804 // The poster uses show/hide so can simply resize it. 4805 this.internal.poster.jq.css({'width': this.status.width, 'height': this.status.height}); 4806 4807 // Video html or flash resized if necessary at this time, or if native video controls being used. 4808 if(!this.status.waitForPlay && this.html.active && this.status.video || this.html.video.available && this.html.used && this.status.nativeVideoControls) { 4809 this.internal.video.jq.css({'width': this.status.width, 'height': this.status.height}); 4810 } 4811 else if(!this.status.waitForPlay && this.flash.active && this.status.video) { 4812 this.internal.flash.jq.css({'width': this.status.width, 'height': this.status.height}); 4813 } 4814 }, 4815 _updateAutohide: function() { 4816 var self = this, 4817 event = "mousemove.jPlayer", 4818 namespace = ".jPlayerAutohide", 4819 eventType = event + namespace, 4820 handler = function(event) { 4821 var moved = false, 4822 deltaX, deltaY; 4823 if(typeof self.internal.mouse !== "undefined") { 4824 //get the change from last position to this position 4825 deltaX = self.internal.mouse.x - event.pageX; 4826 deltaY = self.internal.mouse.y - event.pageY; 4827 moved = (Math.floor(deltaX) > 0) || (Math.floor(deltaY)>0); 4828 } else { 4829 moved = true; 4830 } 4831 // store current position for next method execution 4832 self.internal.mouse = { 4833 x : event.pageX, 4834 y : event.pageY 4835 }; 4836 // if mouse has been actually moved, do the gui fadeIn/fadeOut 4837 if (moved) { 4838 self.css.jq.gui.fadeIn(self.options.autohide.fadeIn, function() { 4839 clearTimeout(self.internal.autohideId); 4840 self.internal.autohideId = setTimeout( function() { 4841 self.css.jq.gui.fadeOut(self.options.autohide.fadeOut); 4842 }, self.options.autohide.hold); 4843 }); 4844 } 4845 }; 4846 4847 if(this.css.jq.gui.length) { 4848 4849 // End animations first so that its callback is executed now. 4850 // Otherwise an in progress fadeIn animation still has the callback to fadeOut again. 4851 this.css.jq.gui.stop(true, true); 4852 4853 // Removes the fadeOut operation from the fadeIn callback. 4854 clearTimeout(this.internal.autohideId); 4855 // undefine mouse 4856 delete this.internal.mouse; 4857 4858 this.element.unbind(namespace); 4859 this.css.jq.gui.unbind(namespace); 4860 4861 if(!this.status.nativeVideoControls) { 4862 if(this.options.fullWindow && this.options.autohide.full || !this.options.fullWindow && this.options.autohide.restored) { 4863 this.element.bind(eventType, handler); 4864 this.css.jq.gui.bind(eventType, handler); 4865 this.css.jq.gui.hide(); 4866 } else { 4867 this.css.jq.gui.show(); 4868 } 4869 } else { 4870 this.css.jq.gui.hide(); 4871 } 4872 } 4873 }, 4874 fullScreen: function(event) { 4875 var guiAction = typeof event === "object"; // Flags GUI click events so we know this was not a direct command, but an action taken by the user on the GUI. 4876 if(guiAction && this.options.useStateClassSkin && this.options.fullScreen) { 4877 this._setOption("fullScreen", false); 4878 } else { 4879 this._setOption("fullScreen", true); 4880 } 4881 }, 4882 restoreScreen: function() { 4883 this._setOption("fullScreen", false); 4884 }, 4885 _fullscreenAddEventListeners: function() { 4886 var self = this, 4887 fs = $.jPlayer.nativeFeatures.fullscreen; 4888 4889 if(fs.api.fullscreenEnabled) { 4890 if(fs.event.fullscreenchange) { 4891 // Create the event handler function and store it for removal. 4892 if(typeof this.internal.fullscreenchangeHandler !== 'function') { 4893 this.internal.fullscreenchangeHandler = function() { 4894 self._fullscreenchange(); 4895 }; 4896 } 4897 document.addEventListener(fs.event.fullscreenchange, this.internal.fullscreenchangeHandler, false); 4898 } 4899 // No point creating handler for fullscreenerror. 4900 // Either logic avoids fullscreen occurring (w3c/moz), or their is no event on the browser (webkit). 4901 } 4902 }, 4903 _fullscreenRemoveEventListeners: function() { 4904 var fs = $.jPlayer.nativeFeatures.fullscreen; 4905 if(this.internal.fullscreenchangeHandler) { 4906 document.removeEventListener(fs.event.fullscreenchange, this.internal.fullscreenchangeHandler, false); 4907 } 4908 }, 4909 _fullscreenchange: function() { 4910 // If nothing is fullscreen, then we cannot be in fullscreen mode. 4911 if(this.options.fullScreen && !$.jPlayer.nativeFeatures.fullscreen.api.fullscreenElement()) { 4912 this._setOption("fullScreen", false); 4913 } 4914 }, 4915 _requestFullscreen: function() { 4916 // Either the container or the jPlayer div 4917 var e = this.ancestorJq.length ? this.ancestorJq[0] : this.element[0], 4918 fs = $.jPlayer.nativeFeatures.fullscreen; 4919 4920 // This method needs the video element. For iOS and Android. 4921 if(fs.used.webkitVideo) { 4922 e = this.htmlElement.video; 4923 } 4924 4925 if(fs.api.fullscreenEnabled) { 4926 fs.api.requestFullscreen(e); 4927 } 4928 }, 4929 _exitFullscreen: function() { 4930 4931 var fs = $.jPlayer.nativeFeatures.fullscreen, 4932 e; 4933 4934 // This method needs the video element. For iOS and Android. 4935 if(fs.used.webkitVideo) { 4936 e = this.htmlElement.video; 4937 } 4938 4939 if(fs.api.fullscreenEnabled) { 4940 fs.api.exitFullscreen(e); 4941 } 4942 }, 4943 _html_initMedia: function(media) { 4944 // Remove any existing track elements 4945 var $media = $(this.htmlElement.media).empty(); 4946 4947 // Create any track elements given with the media, as an Array of track Objects. 4948 $.each(media.track || [], function(i,v) { 4949 var track = document.createElement('track'); 4950 track.setAttribute("kind", v.kind ? v.kind : ""); 4951 track.setAttribute("src", v.src ? v.src : ""); 4952 track.setAttribute("srclang", v.srclang ? v.srclang : ""); 4953 track.setAttribute("label", v.label ? v.label : ""); 4954 if(v.def) { 4955 track.setAttribute("default", v.def); 4956 } 4957 $media.append(track); 4958 }); 4959 4960 this.htmlElement.media.src = this.status.src; 4961 4962 if(this.options.preload !== 'none') { 4963 this._html_load(); // See function for comments 4964 } 4965 this._trigger($.jPlayer.event.timeupdate); // The flash generates this event for its solution. 4966 }, 4967 _html_setFormat: function(media) { 4968 var self = this; 4969 // Always finds a format due to checks in setMedia() 4970 $.each(this.formats, function(priority, format) { 4971 if(self.html.support[format] && media[format]) { 4972 self.status.src = media[format]; 4973 self.status.format[format] = true; 4974 self.status.formatType = format; 4975 return false; 4976 } 4977 }); 4978 }, 4979 _html_setAudio: function(media) { 4980 this._html_setFormat(media); 4981 this.htmlElement.media = this.htmlElement.audio; 4982 this._html_initMedia(media); 4983 }, 4984 _html_setVideo: function(media) { 4985 this._html_setFormat(media); 4986 if(this.status.nativeVideoControls) { 4987 this.htmlElement.video.poster = this._validString(media.poster) ? media.poster : ""; 4988 } 4989 this.htmlElement.media = this.htmlElement.video; 4990 this._html_initMedia(media); 4991 }, 4992 _html_resetMedia: function() { 4993 if(this.htmlElement.media) { 4994 if(this.htmlElement.media.id === this.internal.video.id && !this.status.nativeVideoControls) { 4995 this.internal.video.jq.css({'width':'0px', 'height':'0px'}); 4996 } 4997 this.htmlElement.media.pause(); 4998 } 4999 }, 5000 _html_clearMedia: function() { 5001 if(this.htmlElement.media) { 5002 this.htmlElement.media.src = "about:blank"; 5003 // The following load() is only required for Firefox 3.6 (PowerMacs). 5004 // Recent HTMl5 browsers only require the src change. Due to changes in W3C spec and load() effect. 5005 this.htmlElement.media.load(); // Stops an old, "in progress" download from continuing the download. Triggers the loadstart, error and emptied events, due to the empty src. Also an abort event if a download was in progress. 5006 } 5007 }, 5008 _html_load: function() { 5009 // This function remains to allow the early HTML5 browsers to work, such as Firefox 3.6 5010 // A change in the W3C spec for the media.load() command means that this is no longer necessary. 5011 // This command should be removed and actually causes minor undesirable effects on some browsers. Such as loading the whole file and not only the metadata. 5012 if(this.status.waitForLoad) { 5013 this.status.waitForLoad = false; 5014 this.htmlElement.media.load(); 5015 } 5016 clearTimeout(this.internal.htmlDlyCmdId); 5017 }, 5018 _html_play: function(time) { 5019 var self = this, 5020 media = this.htmlElement.media; 5021 5022 this.androidFix.pause = false; // Cancel the pause fix. 5023 5024 this._html_load(); // Loads if required and clears any delayed commands. 5025 5026 // Setup the Android Fix. 5027 if(this.androidFix.setMedia) { 5028 this.androidFix.play = true; 5029 this.androidFix.time = time; 5030 5031 } else if(!isNaN(time)) { 5032 5033 // Attempt to play it, since iOS has been ignoring commands 5034 if(this.internal.cmdsIgnored) { 5035 media.play(); 5036 } 5037 5038 try { 5039 // !media.seekable is for old HTML5 browsers, like Firefox 3.6. 5040 // Checking seekable.length is important for iOS6 to work with setMedia().play(time) 5041 if(!media.seekable || typeof media.seekable === "object" && media.seekable.length > 0) { 5042 media.currentTime = time; 5043 media.play(); 5044 } else { 5045 throw 1; 5046 } 5047 } catch(err) { 5048 this.internal.htmlDlyCmdId = setTimeout(function() { 5049 self.play(time); 5050 }, 250); 5051 return; // Cancel execution and wait for the delayed command. 5052 } 5053 } else { 5054 media.play(); 5055 } 5056 this._html_checkWaitForPlay(); 5057 }, 5058 _html_pause: function(time) { 5059 var self = this, 5060 media = this.htmlElement.media; 5061 5062 this.androidFix.play = false; // Cancel the play fix. 5063 5064 if(time > 0) { // We do not want the stop() command, which does pause(0), causing a load operation. 5065 this._html_load(); // Loads if required and clears any delayed commands. 5066 } else { 5067 clearTimeout(this.internal.htmlDlyCmdId); 5068 } 5069 5070 // Order of these commands is important for Safari (Win) and IE9. Pause then change currentTime. 5071 media.pause(); 5072 5073 // Setup the Android Fix. 5074 if(this.androidFix.setMedia) { 5075 this.androidFix.pause = true; 5076 this.androidFix.time = time; 5077 5078 } else if(!isNaN(time)) { 5079 try { 5080 if(!media.seekable || typeof media.seekable === "object" && media.seekable.length > 0) { 5081 media.currentTime = time; 5082 } else { 5083 throw 1; 5084 } 5085 } catch(err) { 5086 this.internal.htmlDlyCmdId = setTimeout(function() { 5087 self.pause(time); 5088 }, 250); 5089 return; // Cancel execution and wait for the delayed command. 5090 } 5091 } 5092 if(time > 0) { // Avoids a setMedia() followed by stop() or pause(0) hiding the video play button. 5093 this._html_checkWaitForPlay(); 5094 } 5095 }, 5096 _html_playHead: function(percent) { 5097 var self = this, 5098 media = this.htmlElement.media; 5099 5100 this._html_load(); // Loads if required and clears any delayed commands. 5101 5102 // This playHead() method needs a refactor to apply the android fix. 5103 5104 try { 5105 if(typeof media.seekable === "object" && media.seekable.length > 0) { 5106 media.currentTime = percent * media.seekable.end(media.seekable.length-1) / 100; 5107 } else if(media.duration > 0 && !isNaN(media.duration)) { 5108 media.currentTime = percent * media.duration / 100; 5109 } else { 5110 throw "e"; 5111 } 5112 } catch(err) { 5113 this.internal.htmlDlyCmdId = setTimeout(function() { 5114 self.playHead(percent); 5115 }, 250); 5116 return; // Cancel execution and wait for the delayed command. 5117 } 5118 if(!this.status.waitForLoad) { 5119 this._html_checkWaitForPlay(); 5120 } 5121 }, 5122 _html_checkWaitForPlay: function() { 5123 if(this.status.waitForPlay) { 5124 this.status.waitForPlay = false; 5125 if(this.css.jq.videoPlay.length) { 5126 this.css.jq.videoPlay.hide(); 5127 } 5128 if(this.status.video) { 5129 this.internal.poster.jq.hide(); 5130 this.internal.video.jq.css({'width': this.status.width, 'height': this.status.height}); 5131 } 5132 } 5133 }, 5134 _html_setProperty: function(property, value) { 5135 if(this.html.audio.available) { 5136 this.htmlElement.audio[property] = value; 5137 } 5138 if(this.html.video.available) { 5139 this.htmlElement.video[property] = value; 5140 } 5141 }, 5142 _aurora_setAudio: function(media) { 5143 var self = this; 5144 5145 // Always finds a format due to checks in setMedia() 5146 $.each(this.formats, function(priority, format) { 5147 if(self.aurora.support[format] && media[format]) { 5148 self.status.src = media[format]; 5149 self.status.format[format] = true; 5150 self.status.formatType = format; 5151 5152 return false; 5153 } 5154 }); 5155 5156 this.aurora.player = new AV.Player.fromURL(this.status.src); 5157 this._addAuroraEventListeners(this.aurora.player, this.aurora); 5158 5159 if(this.options.preload === 'auto') { 5160 this._aurora_load(); 5161 this.status.waitForLoad = false; 5162 } 5163 }, 5164 _aurora_resetMedia: function() { 5165 if (this.aurora.player) { 5166 this.aurora.player.stop(); 5167 } 5168 }, 5169 _aurora_clearMedia: function() { 5170 // Nothing to clear. 5171 }, 5172 _aurora_load: function() { 5173 if(this.status.waitForLoad) { 5174 this.status.waitForLoad = false; 5175 this.aurora.player.preload(); 5176 } 5177 }, 5178 _aurora_play: function(time) { 5179 if (!this.status.waitForLoad) { 5180 if (!isNaN(time)) { 5181 this.aurora.player.seek(time); 5182 } 5183 } 5184 if (!this.aurora.player.playing) { 5185 this.aurora.player.play(); 5186 } 5187 this.status.waitForLoad = false; 5188 this._aurora_checkWaitForPlay(); 5189 5190 // No event from the player, update UI now. 5191 this._updateButtons(true); 5192 this._trigger($.jPlayer.event.play); 5193 }, 5194 _aurora_pause: function(time) { 5195 if (!isNaN(time)) { 5196 this.aurora.player.seek(time * 1000); 5197 } 5198 this.aurora.player.pause(); 5199 5200 if(time > 0) { // Avoids a setMedia() followed by stop() or pause(0) hiding the video play button. 5201 this._aurora_checkWaitForPlay(); 5202 } 5203 5204 // No event from the player, update UI now. 5205 this._updateButtons(false); 5206 this._trigger($.jPlayer.event.pause); 5207 }, 5208 _aurora_playHead: function(percent) { 5209 if(this.aurora.player.duration > 0) { 5210 // The seek() sould be in milliseconds, but the only codec that works with seek (aac.js) uses seconds. 5211 this.aurora.player.seek(percent * this.aurora.player.duration / 100); // Using seconds 5212 } 5213 5214 if(!this.status.waitForLoad) { 5215 this._aurora_checkWaitForPlay(); 5216 } 5217 }, 5218 _aurora_checkWaitForPlay: function() { 5219 if(this.status.waitForPlay) { 5220 this.status.waitForPlay = false; 5221 } 5222 }, 5223 _aurora_volume: function(v) { 5224 this.aurora.player.volume = v * 100; 5225 }, 5226 _aurora_mute: function(m) { 5227 if (m) { 5228 this.aurora.properties.lastvolume = this.aurora.player.volume; 5229 this.aurora.player.volume = 0; 5230 } else { 5231 this.aurora.player.volume = this.aurora.properties.lastvolume; 5232 } 5233 this.aurora.properties.muted = m; 5234 }, 5235 _flash_setAudio: function(media) { 5236 var self = this; 5237 try { 5238 // Always finds a format due to checks in setMedia() 5239 $.each(this.formats, function(priority, format) { 5240 if(self.flash.support[format] && media[format]) { 5241 switch (format) { 5242 case "m4a" : 5243 case "fla" : 5244 self._getMovie().fl_setAudio_m4a(media[format]); 5245 break; 5246 case "mp3" : 5247 self._getMovie().fl_setAudio_mp3(media[format]); 5248 break; 5249 case "rtmpa": 5250 self._getMovie().fl_setAudio_rtmp(media[format]); 5251 break; 5252 } 5253 self.status.src = media[format]; 5254 self.status.format[format] = true; 5255 self.status.formatType = format; 5256 return false; 5257 } 5258 }); 5259 5260 if(this.options.preload === 'auto') { 5261 this._flash_load(); 5262 this.status.waitForLoad = false; 5263 } 5264 } catch(err) { this._flashError(err); } 5265 }, 5266 _flash_setVideo: function(media) { 5267 var self = this; 5268 try { 5269 // Always finds a format due to checks in setMedia() 5270 $.each(this.formats, function(priority, format) { 5271 if(self.flash.support[format] && media[format]) { 5272 switch (format) { 5273 case "m4v" : 5274 case "flv" : 5275 self._getMovie().fl_setVideo_m4v(media[format]); 5276 break; 5277 case "rtmpv": 5278 self._getMovie().fl_setVideo_rtmp(media[format]); 5279 break; 5280 } 5281 self.status.src = media[format]; 5282 self.status.format[format] = true; 5283 self.status.formatType = format; 5284 return false; 5285 } 5286 }); 5287 5288 if(this.options.preload === 'auto') { 5289 this._flash_load(); 5290 this.status.waitForLoad = false; 5291 } 5292 } catch(err) { this._flashError(err); } 5293 }, 5294 _flash_resetMedia: function() { 5295 this.internal.flash.jq.css({'width':'0px', 'height':'0px'}); // Must do via CSS as setting attr() to zero causes a jQuery error in IE. 5296 this._flash_pause(NaN); 5297 }, 5298 _flash_clearMedia: function() { 5299 try { 5300 this._getMovie().fl_clearMedia(); 5301 } catch(err) { this._flashError(err); } 5302 }, 5303 _flash_load: function() { 5304 try { 5305 this._getMovie().fl_load(); 5306 } catch(err) { this._flashError(err); } 5307 this.status.waitForLoad = false; 5308 }, 5309 _flash_play: function(time) { 5310 try { 5311 this._getMovie().fl_play(time); 5312 } catch(err) { this._flashError(err); } 5313 this.status.waitForLoad = false; 5314 this._flash_checkWaitForPlay(); 5315 }, 5316 _flash_pause: function(time) { 5317 try { 5318 this._getMovie().fl_pause(time); 5319 } catch(err) { this._flashError(err); } 5320 if(time > 0) { // Avoids a setMedia() followed by stop() or pause(0) hiding the video play button. 5321 this.status.waitForLoad = false; 5322 this._flash_checkWaitForPlay(); 5323 } 5324 }, 5325 _flash_playHead: function(p) { 5326 try { 5327 this._getMovie().fl_play_head(p); 5328 } catch(err) { this._flashError(err); } 5329 if(!this.status.waitForLoad) { 5330 this._flash_checkWaitForPlay(); 5331 } 5332 }, 5333 _flash_checkWaitForPlay: function() { 5334 if(this.status.waitForPlay) { 5335 this.status.waitForPlay = false; 5336 if(this.css.jq.videoPlay.length) { 5337 this.css.jq.videoPlay.hide(); 5338 } 5339 if(this.status.video) { 5340 this.internal.poster.jq.hide(); 5341 this.internal.flash.jq.css({'width': this.status.width, 'height': this.status.height}); 5342 } 5343 } 5344 }, 5345 _flash_volume: function(v) { 5346 try { 5347 this._getMovie().fl_volume(v); 5348 } catch(err) { this._flashError(err); } 5349 }, 5350 _flash_mute: function(m) { 5351 try { 5352 this._getMovie().fl_mute(m); 5353 } catch(err) { this._flashError(err); } 5354 }, 5355 _getMovie: function() { 5356 return document[this.internal.flash.id]; 5357 }, 5358 _getFlashPluginVersion: function() { 5359 5360 // _getFlashPluginVersion() code influenced by: 5361 // - FlashReplace 1.01: http://code.google.com/p/flashreplace/ 5362 // - SWFObject 2.2: http://code.google.com/p/swfobject/ 5363 5364 var version = 0, 5365 flash; 5366 if(window.ActiveXObject) { 5367 try { 5368 flash = new ActiveXObject("ShockwaveFlash.ShockwaveFlash"); 5369 if (flash) { // flash will return null when ActiveX is disabled 5370 var v = flash.GetVariable("$version"); 5371 if(v) { 5372 v = v.split(" ")[1].split(","); 5373 version = parseInt(v[0], 10) + "." + parseInt(v[1], 10); 5374 } 5375 } 5376 } catch(e) {} 5377 } 5378 else if(navigator.plugins && navigator.mimeTypes.length > 0) { 5379 flash = navigator.plugins["Shockwave Flash"]; 5380 if(flash) { 5381 version = navigator.plugins["Shockwave Flash"].description.replace(/.*\s(\d+\.\d+).*/, "$1"); 5382 } 5383 } 5384 return version * 1; // Converts to a number 5385 }, 5386 _checkForFlash: function (version) { 5387 var flashOk = false; 5388 if(this._getFlashPluginVersion() >= version) { 5389 flashOk = true; 5390 } 5391 return flashOk; 5392 }, 5393 _validString: function(url) { 5394 return (url && typeof url === "string"); // Empty strings return false 5395 }, 5396 _limitValue: function(value, min, max) { 5397 return (value < min) ? min : ((value > max) ? max : value); 5398 }, 5399 _urlNotSetError: function(context) { 5400 this._error( { 5401 type: $.jPlayer.error.URL_NOT_SET, 5402 context: context, 5403 message: $.jPlayer.errorMsg.URL_NOT_SET, 5404 hint: $.jPlayer.errorHint.URL_NOT_SET 5405 }); 5406 }, 5407 _flashError: function(error) { 5408 var errorType; 5409 if(!this.internal.ready) { 5410 errorType = "FLASH"; 5411 } else { 5412 errorType = "FLASH_DISABLED"; 5413 } 5414 this._error( { 5415 type: $.jPlayer.error[errorType], 5416 context: this.internal.flash.swf, 5417 message: $.jPlayer.errorMsg[errorType] + error.message, 5418 hint: $.jPlayer.errorHint[errorType] 5419 }); 5420 // Allow the audio player to recover if display:none and then shown again, or with position:fixed on Firefox. 5421 // This really only affects audio in a media player, as an audio player could easily move the jPlayer element away from such issues. 5422 this.internal.flash.jq.css({'width':'1px', 'height':'1px'}); 5423 }, 5424 _error: function(error) { 5425 this._trigger($.jPlayer.event.error, error); 5426 if(this.options.errorAlerts) { 5427 this._alert("Error!" + (error.message ? "\n" + error.message : "") + (error.hint ? "\n" + error.hint : "") + "\nContext: " + error.context); 5428 } 5429 }, 5430 _warning: function(warning) { 5431 this._trigger($.jPlayer.event.warning, undefined, warning); 5432 if(this.options.warningAlerts) { 5433 this._alert("Warning!" + (warning.message ? "\n" + warning.message : "") + (warning.hint ? "\n" + warning.hint : "") + "\nContext: " + warning.context); 5434 } 5435 }, 5436 _alert: function(message) { 5437 var msg = "jPlayer " + this.version.script + " : id='" + this.internal.self.id +"' : " + message; 5438 if(!this.options.consoleAlerts) { 5439 alert(msg); 5440 } else if(window.console && window.console.log) { 5441 window.console.log(msg); 5442 } 5443 }, 5444 _emulateHtmlBridge: function() { 5445 var self = this; 5446 5447 // Emulate methods on jPlayer's DOM element. 5448 $.each( $.jPlayer.emulateMethods.split(/\s+/g), function(i, name) { 5449 self.internal.domNode[name] = function(arg) { 5450 self[name](arg); 5451 }; 5452 5453 }); 5454 5455 // Bubble jPlayer events to its DOM element. 5456 $.each($.jPlayer.event, function(eventName,eventType) { 5457 var nativeEvent = true; 5458 $.each( $.jPlayer.reservedEvent.split(/\s+/g), function(i, name) { 5459 if(name === eventName) { 5460 nativeEvent = false; 5461 return false; 5462 } 5463 }); 5464 if(nativeEvent) { 5465 self.element.bind(eventType + ".jPlayer.jPlayerHtml", function() { // With .jPlayer & .jPlayerHtml namespaces. 5466 self._emulateHtmlUpdate(); 5467 var domEvent = document.createEvent("Event"); 5468 domEvent.initEvent(eventName, false, true); 5469 self.internal.domNode.dispatchEvent(domEvent); 5470 }); 5471 } 5472 // The error event would require a special case 5473 }); 5474 5475 // IE9 has a readyState property on all elements. The document should have it, but all (except media) elements inherit it in IE9. This conflicts with Popcorn, which polls the readyState. 5476 }, 5477 _emulateHtmlUpdate: function() { 5478 var self = this; 5479 5480 $.each( $.jPlayer.emulateStatus.split(/\s+/g), function(i, name) { 5481 self.internal.domNode[name] = self.status[name]; 5482 }); 5483 $.each( $.jPlayer.emulateOptions.split(/\s+/g), function(i, name) { 5484 self.internal.domNode[name] = self.options[name]; 5485 }); 5486 }, 5487 _destroyHtmlBridge: function() { 5488 var self = this; 5489 5490 // Bridge event handlers are also removed by destroy() through .jPlayer namespace. 5491 this.element.unbind(".jPlayerHtml"); // Remove all event handlers created by the jPlayer bridge. So you can change the emulateHtml option. 5492 5493 // Remove the methods and properties 5494 var emulated = $.jPlayer.emulateMethods + " " + $.jPlayer.emulateStatus + " " + $.jPlayer.emulateOptions; 5495 $.each( emulated.split(/\s+/g), function(i, name) { 5496 delete self.internal.domNode[name]; 5497 }); 5498 } 5499 }; 5500 5501 $.jPlayer.error = { 5502 FLASH: "e_flash", 5503 FLASH_DISABLED: "e_flash_disabled", 5504 NO_SOLUTION: "e_no_solution", 5505 NO_SUPPORT: "e_no_support", 5506 URL: "e_url", 5507 URL_NOT_SET: "e_url_not_set", 5508 VERSION: "e_version" 5509 }; 5510 5511 $.jPlayer.errorMsg = { 5512 FLASH: "jPlayer's Flash fallback is not configured correctly, or a command was issued before the jPlayer Ready event. Details: ", // Used in: _flashError() 5513 FLASH_DISABLED: "jPlayer's Flash fallback has been disabled by the browser due to the CSS rules you have used. Details: ", // Used in: _flashError() 5514 NO_SOLUTION: "No solution can be found by jPlayer in this browser. Neither HTML nor Flash can be used.", // Used in: _init() 5515 NO_SUPPORT: "It is not possible to play any media format provided in setMedia() on this browser using your current options.", // Used in: setMedia() 5516 URL: "Media URL could not be loaded.", // Used in: jPlayerFlashEvent() and _addHtmlEventListeners() 5517 URL_NOT_SET: "Attempt to issue media playback commands, while no media url is set.", // Used in: load(), play(), pause(), stop() and playHead() 5518 VERSION: "jPlayer " + $.jPlayer.prototype.version.script + " needs Jplayer.swf version " + $.jPlayer.prototype.version.needFlash + " but found " // Used in: jPlayerReady() 5519 }; 5520 5521 $.jPlayer.errorHint = { 5522 FLASH: "Check your swfPath option and that Jplayer.swf is there.", 5523 FLASH_DISABLED: "Check that you have not display:none; the jPlayer entity or any ancestor.", 5524 NO_SOLUTION: "Review the jPlayer options: support and supplied.", 5525 NO_SUPPORT: "Video or audio formats defined in the supplied option are missing.", 5526 URL: "Check media URL is valid.", 5527 URL_NOT_SET: "Use setMedia() to set the media URL.", 5528 VERSION: "Update jPlayer files." 5529 }; 5530 5531 $.jPlayer.warning = { 5532 CSS_SELECTOR_COUNT: "e_css_selector_count", 5533 CSS_SELECTOR_METHOD: "e_css_selector_method", 5534 CSS_SELECTOR_STRING: "e_css_selector_string", 5535 OPTION_KEY: "e_option_key" 5536 }; 5537 5538 $.jPlayer.warningMsg = { 5539 CSS_SELECTOR_COUNT: "The number of css selectors found did not equal one: ", 5540 CSS_SELECTOR_METHOD: "The methodName given in jPlayer('cssSelector') is not a valid jPlayer method.", 5541 CSS_SELECTOR_STRING: "The methodCssSelector given in jPlayer('cssSelector') is not a String or is empty.", 5542 OPTION_KEY: "The option requested in jPlayer('option') is undefined." 5543 }; 5544 5545 $.jPlayer.warningHint = { 5546 CSS_SELECTOR_COUNT: "Check your css selector and the ancestor.", 5547 CSS_SELECTOR_METHOD: "Check your method name.", 5548 CSS_SELECTOR_STRING: "Check your css selector is a string.", 5549 OPTION_KEY: "Check your option name." 5550 }; 5551})); 5552 5553}); 5554