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('&amp;').split('<').join('&lt;').split('>').join('&gt;').split('"').join('&quot;');
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