PageSourceSearch

https://www.gigamic.com/modules/productvideos/views/js/jquery.jplayer.js

js gigamic.com collected 2026-09-24 18:23:33 UTC 159,059 bytes, 3,506 lines download raw bytes

1/*
2 * jPlayer Plugin for jQuery JavaScript Library
3 * http://www.jplayer.org
4 *
5 * Copyright (c) 2009 - 2025 Happyworm Ltd
6 * Licensed under the MIT license.
7 * http://opensource.org/licenses/MIT
8 *
9 * Author: Mark J Panaghiston
10 * Version: 2.9.2
11 * Date: 14th July 2025
12 */
13
14/* Support for Zepto 1.0 compiled with optional data module.
15 * For AMD or NODE/CommonJS support, you will need to manually switch the related 2 lines in the code below.
16 * Search terms: "jQuery Switch" and "Zepto Switch"
17 */
18
19(function(root, factory) {
20    if (typeof define === 'function' && define.amd) {
21        // AMD. Register as an anonymous module.
22        define(['jquery'], factory); // jQuery Switch
23        // define(['zepto'], factory); // Zepto Switch
24    } else if (typeof exports === 'object') {
25        // Node/CommonJS
26        factory(require('jquery')); // jQuery Switch
27        //factory(require('zepto')); // Zepto Switch
28    } else {
29        // Browser globals
30        if (root.jQuery) { // Use jQuery if available
31            factory(root.jQuery);
32        } else { // Otherwise, use Zepto
33            factory(root.Zepto);
34        }
35    }
36}(this, function($, undefined) {
37
38    // Adapted from jquery.ui.widget.js (1.8.7): $.widget.bridge - Tweaked $.data(this,XYZ) to $(this).data(XYZ) for Zepto
39    $.fn.jPlayer = function(options) {
40        var name = "jPlayer";
41        var isMethodCall = typeof options === "string",
42            args = Array.prototype.slice.call(arguments, 1),
43            returnValue = this;
44
45        // allow multiple hashes to be passed on init
46        options = !isMethodCall && args.length ?
47            $.extend.apply(null, [true, options].concat(args)) :
48            options;
49
50        // prevent calls to internal methods
51        if (isMethodCall && options.charAt(0) === "_") {
52            return returnValue;
53        }
54
55        if (isMethodCall) {
56            this.each(function() {
57                var instance = $(this).data(name),
58                    methodValue = instance && $.isFunction(instance[options]) ?
59                    instance[options].apply(instance, args) :
60                    instance;
61                if (methodValue !== instance && methodValue !== undefined) {
62                    returnValue = methodValue;
63                    return false;
64                }
65            });
66        } else {
67            this.each(function() {
68                var instance = $(this).data(name);
69                if (instance) {
70                    // 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.
71                    instance.option(options || {}); // The new constructor only changes the options. Changing options only has basic support atm.
72                } else {
73                    $(this).data(name, new $.jPlayer(options, this));
74                }
75            });
76        }
77
78        return returnValue;
79    };
80
81    $.jPlayer = function(options, element) {
82        // allow instantiation without initializing for simple inheritance
83        if (arguments.length) {
84            this.element = $(element);
85            this.options = $.extend(true, {},
86                this.options,
87                options
88            );
89            var self = this;
90            this.element.bind("remove.jPlayer", function() {
91                self.destroy();
92            });
93            this._init();
94        }
95    };
96    // End of: (Adapted from jquery.ui.widget.js (1.8.7))
97
98    // Zepto is missing one of the animation methods.
99    if (typeof $.fn.stop !== 'function') {
100        $.fn.stop = function() {};
101    }
102
103    // Emulated HTML5 methods and properties
104    $.jPlayer.emulateMethods = "load play pause";
105    $.jPlayer.emulateStatus = "src readyState networkState currentTime duration paused ended playbackRate";
106    $.jPlayer.emulateOptions = "muted volume";
107
108    // Reserved event names generated by jPlayer that are not part of the HTML5 Media element spec
109    $.jPlayer.reservedEvent = "ready flashreset resize repeat error warning";
110
111    // Events generated by jPlayer
112    $.jPlayer.event = {};
113    $.each(
114        [
115            'ready',
116            'setmedia', // Fires when the media is set
117            '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.
118            'resize', // Occurs when the size changes through a full/restore screen operation or if the size/sizeFull options are changed.
119            'repeat', // Occurs when the repeat status changes. Usually through clicks on the repeat button of the interface.
120            'click', // Occurs when the user clicks on one of the following: poster image, html video, flash video.
121            'error', // Event error code in event.jPlayer.error.type. See $.jPlayer.error
122            'warning', // Event warning code in event.jPlayer.warning.type. See $.jPlayer.warning
123
124            // Other events match HTML5 spec.
125            'loadstart',
126            'progress',
127            'suspend',
128            'abort',
129            'emptied',
130            'stalled',
131            'play',
132            'pause',
133            'loadedmetadata',
134            'loadeddata',
135            'waiting',
136            'playing',
137            'canplay',
138            'canplaythrough',
139            'seeking',
140            'seeked',
141            'timeupdate',
142            'ended',
143            'ratechange',
144            'durationchange',
145            'volumechange'
146        ],
147        function() {
148            $.jPlayer.event[this] = 'jPlayer_' + this;
149        }
150    );
151
152    $.jPlayer.htmlEvent = [ // These HTML events are bubbled through to the jPlayer event, without any internal action.
153        "loadstart",
154        // "progress", // jPlayer uses internally before bubbling.
155        // "suspend", // jPlayer uses internally before bubbling.
156        "abort",
157        // "error", // jPlayer uses internally before bubbling.
158        "emptied",
159        "stalled",
160        // "play", // jPlayer uses internally before bubbling.
161        // "pause", // jPlayer uses internally before bubbling.
162        "loadedmetadata",
163        // "loadeddata", // jPlayer uses internally before bubbling.
164        // "waiting", // jPlayer uses internally before bubbling.
165        // "playing", // jPlayer uses internally before bubbling.
166        "canplay",
167        "canplaythrough"
168        // "seeking", // jPlayer uses internally before bubbling.
169        // "seeked", // jPlayer uses internally before bubbling.
170        // "timeupdate", // jPlayer uses internally before bubbling.
171        // "ended", // jPlayer uses internally before bubbling.
172        // "ratechange" // jPlayer uses internally before bubbling.
173        // "durationchange" // jPlayer uses internally before bubbling.
174        // "volumechange" // jPlayer uses internally before bubbling.
175    ];
176
177    $.jPlayer.pause = function() {
178        $.jPlayer.prototype.destroyRemoved();
179        $.each($.jPlayer.prototype.instances, function(i, element) {
180            if (element.data("jPlayer").status.srcSet) { // Check that media is set otherwise would cause error event.
181                element.jPlayer("pause");
182            }
183        });
184    };
185
186    // Default for jPlayer option.timeFormat
187    $.jPlayer.timeFormat = {
188        showHour: false,
189        showMin: true,
190        showSec: true,
191        padHour: false,
192        padMin: true,
193        padSec: true,
194        sepHour: ":",
195        sepMin: ":",
196        sepSec: ""
197    };
198    var ConvertTime = function() {
199        this.init();
200    };
201    ConvertTime.prototype = {
202        init: function() {
203            this.options = {
204                timeFormat: $.jPlayer.timeFormat
205            };
206        },
207        time: function(s) { // function used on jPlayer.prototype._convertTime to enable per instance options.
208            s = (s && typeof s === 'number') ? s : 0;
209
210            var myTime = new Date(s * 1000),
211                hour = myTime.getUTCHours(),
212                min = this.options.timeFormat.showHour ? myTime.getUTCMinutes() : myTime.getUTCMinutes() + hour * 60,
213                sec = this.options.timeFormat.showMin ? myTime.getUTCSeconds() : myTime.getUTCSeconds() + min * 60,
214                strHour = (this.options.timeFormat.padHour && hour < 10) ? "0" + hour : hour,
215                strMin = (this.options.timeFormat.padMin && min < 10) ? "0" + min : min,
216                strSec = (this.options.timeFormat.padSec && sec < 10) ? "0" + sec : sec,
217                strTime = "";
218
219            strTime += this.options.timeFormat.showHour ? strHour + this.options.timeFormat.sepHour : "";
220            strTime += this.options.timeFormat.showMin ? strMin + this.options.timeFormat.sepMin : "";
221            strTime += this.options.timeFormat.showSec ? strSec + this.options.timeFormat.sepSec : "";
222
223            return strTime;
224        }
225    };
226    var myConvertTime = new ConvertTime();
227    $.jPlayer.convertTime = function(s) {
228        return myConvertTime.time(s);
229    };
230
231    // Adapting jQuery 1.4.4 code for jQuery.browser. Required since jQuery 1.3.2 does not detect Chrome as webkit.
232    $.jPlayer.uaBrowser = function(userAgent) {
233        var ua = userAgent.toLowerCase();
234
235        // Useragent RegExp
236        var rwebkit = /(webkit)[ \/]([\w.]+)/;
237        var ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/;
238        var rmsie = /(msie) ([\w.]+)/;
239        var rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/;
240
241        var match = rwebkit.exec(ua) ||
242            ropera.exec(ua) ||
243            rmsie.exec(ua) ||
244            ua.indexOf("compatible") < 0 && rmozilla.exec(ua) || [];
245
246        return { browser: match[1] || "", version: match[2] || "0" };
247    };
248
249    // Platform sniffer for detecting mobile devices
250    $.jPlayer.uaPlatform = function(userAgent) {
251        var ua = userAgent.toLowerCase();
252
253        // Useragent RegExp
254        var rplatform = /(ipad|iphone|ipod|android|blackberry|playbook|windows ce|webos)/;
255        var rtablet = /(ipad|playbook)/;
256        var randroid = /(android)/;
257        var rmobile = /(mobile)/;
258
259        var platform = rplatform.exec(ua) || [];
260        var tablet = rtablet.exec(ua) ||
261            !rmobile.exec(ua) && randroid.exec(ua) || [];
262
263        if (platform[1]) {
264            platform[1] = platform[1].replace(/\s/g, "_"); // Change whitespace to underscore. Enables dot notation.
265        }
266
267        return { platform: platform[1] || "", tablet: tablet[1] || "" };
268    };
269
270    $.jPlayer.browser = {};
271    $.jPlayer.platform = {};
272
273    var browserMatch = $.jPlayer.uaBrowser(navigator.userAgent);
274    if (browserMatch.browser) {
275        $.jPlayer.browser[browserMatch.browser] = true;
276        $.jPlayer.browser.version = browserMatch.version;
277    }
278    var platformMatch = $.jPlayer.uaPlatform(navigator.userAgent);
279    if (platformMatch.platform) {
280        $.jPlayer.platform[platformMatch.platform] = true;
281        $.jPlayer.platform.mobile = !platformMatch.tablet;
282        $.jPlayer.platform.tablet = !!platformMatch.tablet;
283    }
284
285    // Internet Explorer (IE) Browser Document Mode Sniffer. Based on code at:
286    // http://msdn.microsoft.com/en-us/library/cc288325%28v=vs.85%29.aspx#GetMode
287    $.jPlayer.getDocMode = function() {
288        var docMode;
289        if ($.jPlayer.browser.msie) {
290            if (document.documentMode) { // IE8 or later
291                docMode = document.documentMode;
292            } else { // IE 5-7
293                docMode = 5; // Assume quirks mode unless proven otherwise
294                if (document.compatMode) {
295                    if (document.compatMode === "CSS1Compat") {
296                        docMode = 7; // standards mode
297                    }
298                }
299            }
300        }
301        return docMode;
302    };
303    $.jPlayer.browser.documentMode = $.jPlayer.getDocMode();
304
305    $.jPlayer.nativeFeatures = {
306        init: function() {
307
308            /* Fullscreen function naming influenced by W3C naming.
309             * No support for: Mozilla Proposal: https://wiki.mozilla.org/Gecko:FullScreenAPI
310             */
311
312            var d = document,
313                v = d.createElement('video'),
314                spec = {
315                    // http://www.w3.org/TR/fullscreen/
316                    w3c: [
317                        'fullscreenEnabled',
318                        'fullscreenElement',
319                        'requestFullscreen',
320                        'exitFullscreen',
321                        'fullscreenchange',
322                        'fullscreenerror'
323                    ],
324                    // https://developer.mozilla.org/en-US/docs/DOM/Using_fullscreen_mode
325                    moz: [
326                        'mozFullScreenEnabled',
327                        'mozFullScreenElement',
328                        'mozRequestFullScreen',
329                        'mozCancelFullScreen',
330                        'mozfullscreenchange',
331                        'mozfullscreenerror'
332                    ],
333                    // http://developer.apple.com/library/safari/#documentation/WebKit/Reference/ElementClassRef/Element/Element.html
334                    // http://developer.apple.com/library/safari/#documentation/UserExperience/Reference/DocumentAdditionsReference/DocumentAdditions/DocumentAdditions.html
335                    webkit: [
336                        '',
337                        'webkitCurrentFullScreenElement',
338                        'webkitRequestFullScreen',
339                        'webkitCancelFullScreen',
340                        'webkitfullscreenchange',
341                        ''
342                    ],
343                    // http://developer.apple.com/library/safari/#documentation/AudioVideo/Reference/HTMLVideoElementClassReference/HTMLVideoElement/HTMLVideoElement.html
344                    // https://developer.apple.com/library/safari/samplecode/HTML5VideoEventFlow/Listings/events_js.html#//apple_ref/doc/uid/DTS40010085-events_js-DontLinkElementID_5
345                    // Events: 'webkitbeginfullscreen' and 'webkitendfullscreen'
346                    webkitVideo: [
347                        'webkitSupportsFullscreen',
348                        'webkitDisplayingFullscreen',
349                        'webkitEnterFullscreen',
350                        'webkitExitFullscreen',
351                        '',
352                        ''
353                    ],
354                    ms: [
355                        '',
356                        'msFullscreenElement',
357                        'msRequestFullscreen',
358                        'msExitFullscreen',
359                        'MSFullscreenChange',
360                        'MSFullscreenError'
361                    ]
362                },
363                specOrder = [
364                    'w3c',
365                    'moz',
366                    'webkit',
367                    'webkitVideo',
368                    'ms'
369                ],
370                fs, i, il;
371
372            this.fullscreen = fs = {
373                support: {
374                    w3c: !!d[spec.w3c[0]],
375                    moz: !!d[spec.moz[0]],
376                    webkit: typeof d[spec.webkit[3]] === 'function',
377                    webkitVideo: typeof v[spec.webkitVideo[2]] === 'function',
378                    ms: typeof v[spec.ms[2]] === 'function'
379                },
380                used: {}
381            };
382
383            // Store the name of the spec being used and as a handy boolean.
384            for (i = 0, il = specOrder.length; i < il; i++) {
385                var n = specOrder[i];
386                if (fs.support[n]) {
387                    fs.spec = n;
388                    fs.used[n] = true;
389                    break;
390                }
391            }
392
393            if (fs.spec) {
394                var s = spec[fs.spec];
395                fs.api = {
396                    fullscreenEnabled: true,
397                    fullscreenElement: function(elem) {
398                        elem = elem ? elem : d; // Video element required for webkitVideo
399                        return elem[s[1]];
400                    },
401                    requestFullscreen: function(elem) {
402                        return elem[s[2]](); // Chrome and Opera want parameter (Element.ALLOW_KEYBOARD_INPUT) but Safari fails if flag used.
403                    },
404                    exitFullscreen: function(elem) {
405                        elem = elem ? elem : d; // Video element required for webkitVideo
406                        return elem[s[3]]();
407                    }
408                };
409                fs.event = {
410                    fullscreenchange: s[4],
411                    fullscreenerror: s[5]
412                };
413            } else {
414                fs.api = {
415                    fullscreenEnabled: false,
416                    fullscreenElement: function() {
417                        return null;
418                    },
419                    requestFullscreen: function() {},
420                    exitFullscreen: function() {}
421                };
422                fs.event = {};
423            }
424        }
425    };
426    $.jPlayer.nativeFeatures.init();
427
428    // The keyboard control system.
429
430    // The current jPlayer instance in focus.
431    $.jPlayer.focus = null;
432
433    // The list of element node names to ignore with key controls.
434    $.jPlayer.keyIgnoreElementNames = "A INPUT TEXTAREA SELECT BUTTON";
435
436    // The function that deals with key presses.
437    var keyBindings = function(event) {
438        var f = $.jPlayer.focus,
439            ignoreKey;
440
441        // A jPlayer instance must be in focus. ie., keyEnabled and the last one played.
442        if (f) {
443            // What generated the key press?
444            $.each($.jPlayer.keyIgnoreElementNames.split(/\s+/g), function(i, name) {
445                // The strings should already be uppercase.
446                if (event.target.nodeName.toUpperCase() === name.toUpperCase()) {
447                    ignoreKey = true;
448                    return false; // exit each.
449                }
450            });
451            if (!ignoreKey) {
452                // See if the key pressed matches any of the bindings.
453                $.each(f.options.keyBindings, function(action, binding) {
454                    // The binding could be a null when the default has been disabled. ie., 1st clause in if()
455                    if (
456                        (binding && $.isFunction(binding.fn)) &&
457                        ((typeof binding.key === 'number' && event.which === binding.key) ||
458                            (typeof binding.key === 'string' && event.key === binding.key))
459                    ) {
460                        event.preventDefault(); // Key being used by jPlayer, so prevent default operation.
461                        binding.fn(f);
462                        return false; // exit each.
463                    }
464                });
465            }
466        }
467    };
468
469    $.jPlayer.keys = function(en) {
470        var event = "keydown.jPlayer";
471        // Remove any binding, just in case enabled more than once.
472        $(document.documentElement).unbind(event);
473        if (en) {
474            $(document.documentElement).bind(event, keyBindings);
475        }
476    };
477
478    // Enable the global key control handler ready for any jPlayer instance with the keyEnabled option enabled.
479    $.jPlayer.keys(true);
480
481    $.jPlayer.prototype = {
482        count: 0, // Static Variable: Change it via prototype.
483        version: { // Static Object
484            script: "2.9.2",
485            needFlash: "2.9.0",
486            flash: "unknown"
487        },
488        options: { // Instanced in $.jPlayer() constructor
489            swfPath: "js", // Path to jquery.jplayer.swf. Can be relative, absolute or server root relative.
490            solution: "html, flash", // Valid solutions: html, flash, aurora. Order defines priority. 1st is highest,
491            supplied: "mp3", // Defines which formats jPlayer will try and support and the priority by the order. 1st is highest,
492            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.
493            preload: 'metadata', // HTML5 Spec values: none, metadata, auto.
494            volume: 0.8, // The volume. Number 0 to 1.
495            muted: false,
496            remainingDuration: false, // When true, the remaining time is shown in the duration GUI element.
497            toggleDuration: false, // When true, clicks on the duration toggle between the duration and remaining display.
498            captureDuration: true, // When true, clicks on the duration are captured and no longer propagate up the DOM.
499            playbackRate: 1,
500            defaultPlaybackRate: 1,
501            minPlaybackRate: 0.5,
502            maxPlaybackRate: 4,
503            wmode: "opaque", // Valid wmode: window, transparent, opaque, direct, gpu. 
504            backgroundColor: "#000000", // To define the jPlayer div and Flash background color.
505            cssSelectorAncestor: "#jp_container_1",
506            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.
507                videoPlay: ".jp-video-play", // *
508                play: ".jp-play",
509                pause: ".jp-pause",
510                stop: ".jp-stop",
511                seekBar: ".jp-seek-bar",
512                playBar: ".jp-play-bar",
513                mute: ".jp-mute",
514                unmute: ".jp-unmute",
515                volumeBar: ".jp-volume-bar",
516                volumeBarValue: ".jp-volume-bar-value",
517                volumeMax: ".jp-volume-max",
518                playbackRateBar: ".jp-playback-rate-bar",
519                playbackRateBarValue: ".jp-playback-rate-bar-value",
520                currentTime: ".jp-current-time",
521                duration: ".jp-duration",
522                title: ".jp-title",
523                fullScreen: ".jp-full-screen", // *
524                restoreScreen: ".jp-restore-screen", // *
525                repeat: ".jp-repeat",
526                repeatOff: ".jp-repeat-off",
527                gui: ".jp-gui", // The interface used with autohide feature.
528                noSolution: ".jp-no-solution" // For error feedback when jPlayer cannot find a solution.
529            },
530            stateClass: { // Classes added to the cssSelectorAncestor to indicate the state.
531                playing: "jp-state-playing",
532                seeking: "jp-state-seeking",
533                muted: "jp-state-muted",
534                looped: "jp-state-looped",
535                fullScreen: "jp-state-full-screen",
536                noVolume: "jp-state-no-volume"
537            },
538            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.
539            autoBlur: true, // GUI control handlers will drop focus after clicks.
540            smoothPlayBar: false, // Smooths the play bar transitions, which affects clicks and short media with big changes per second.
541            fullScreen: false, // Native Full Screen
542            fullWindow: false,
543            autohide: {
544                restored: false, // Controls the interface autohide feature.
545                full: true, // Controls the interface autohide feature.
546                fadeIn: 200, // Milliseconds. The period of the fadeIn anim.
547                fadeOut: 600, // Milliseconds. The period of the fadeOut anim.
548                hold: 1000 // Milliseconds. The period of the pause before autohide beings.
549            },
550            loop: false,
551            repeat: function(event) { // The default jPlayer repeat event handler
552                if (event.jPlayer.options.loop) {
553                    $(this).unbind(".jPlayerRepeat").bind($.jPlayer.event.ended + ".jPlayer.jPlayerRepeat", function() {
554                        $(this).jPlayer("play");
555                    });
556                } else {
557                    $(this).unbind(".jPlayerRepeat");
558                }
559            },
560            nativeVideoControls: {
561                // Works well on standard browsers.
562                // Phone and tablet browsers can have problems with the controls disappearing.
563            },
564            noFullWindow: {
565                msie: /msie [0-6]\./,
566                ipad: /ipad.*?os [0-4]\./,
567                iphone: /iphone/,
568                ipod: /ipod/,
569                android_pad: /android [0-3]\.(?!.*?mobile)/,
570                android_phone: /(?=.*android)(?!.*chrome)(?=.*mobile)/,
571                blackberry: /blackberry/,
572                windows_ce: /windows ce/,
573                iemobile: /iemobile/,
574                webos: /webos/
575            },
576            noVolume: {
577                ipad: /ipad/,
578                iphone: /iphone/,
579                ipod: /ipod/,
580                android_pad: /android(?!.*?mobile)/,
581                android_phone: /android.*?mobile/,
582                blackberry: /blackberry/,
583                windows_ce: /windows ce/,
584                iemobile: /iemobile/,
585                webos: /webos/,
586                playbook: /playbook/
587            },
588            timeFormat: {
589                // Specific time format for this instance. The supported options are defined in $.jPlayer.timeFormat
590                // For the undefined options we use the default from $.jPlayer.timeFormat
591            },
592            keyEnabled: false, // Enables keyboard controls.
593            audioFullScreen: false, // Enables keyboard controls to enter full screen with audio media.
594            keyBindings: { // The key control object, defining the key codes and the functions to execute.
595                // The parameter, f = $.jPlayer.focus, will be checked truethy before attempting to call any of these functions.
596                // Properties may be added to this object, in key/fn pairs, to enable other key controls. EG, for the playlist add-on.
597                play: {
598                    key: 80, // p
599                    fn: function(f) {
600                        if (f.status.paused) {
601                            f.play();
602                        } else {
603                            f.pause();
604                        }
605                    }
606                },
607                fullScreen: {
608                    key: 70, // f
609                    fn: function(f) {
610                        if (f.status.video || f.options.audioFullScreen) {
611                            f._setOption("fullScreen", !f.options.fullScreen);
612                        }
613                    }
614                },
615                muted: {
616                    key: 77, // m
617                    fn: function(f) {
618                        f._muted(!f.options.muted);
619                    }
620                },
621                volumeUp: {
622                    key: 190, // .
623                    fn: function(f) {
624                        f.volume(f.options.volume + 0.1);
625                    }
626                },
627                volumeDown: {
628                    key: 188, // ,
629                    fn: function(f) {
630                        f.volume(f.options.volume - 0.1);
631                    }
632                },
633                loop: {
634                    key: 76, // l
635                    fn: function(f) {
636                        f._loop(!f.options.loop);
637                    }
638                }
639            },
640            verticalVolume: false, // Calculate volume from the bottom of the volume bar. Default is from the left. Also volume affects either width or height.
641            verticalPlaybackRate: false,
642            globalVolume: false, // Set to make volume and muted changes affect all jPlayer instances with this option enabled
643            idPrefix: "jp", // Prefix for the ids of html elements created by jPlayer. For flash, this must not include characters: . - + * / \
644            noConflict: "jQuery",
645            emulateHtml: false, // Emulates the HTML5 Media element on the jPlayer element.
646            consoleAlerts: true, // Alerts are sent to the console.log() instead of alert().
647            errorAlerts: false,
648            warningAlerts: false
649        },
650        optionsAudio: {
651            size: {
652                width: "0px",
653                height: "0px",
654                cssClass: ""
655            },
656            sizeFull: {
657                width: "0px",
658                height: "0px",
659                cssClass: ""
660            }
661        },
662        optionsVideo: {
663            size: {
664                width: "480px",
665                height: "270px",
666                cssClass: "jp-video-270p"
667            },
668            sizeFull: {
669                width: "100%",
670                height: "100%",
671                cssClass: "jp-video-full"
672            }
673        },
674        instances: {}, // Static Object
675        status: { // Instanced in _init()
676            src: "",
677            media: {},
678            paused: true,
679            format: {},
680            formatType: "",
681            waitForPlay: true, // Same as waitForLoad except in case where preloading.
682            waitForLoad: true,
683            srcSet: false,
684            video: false, // True if playing a video
685            seekPercent: 0,
686            currentPercentRelative: 0,
687            currentPercentAbsolute: 0,
688            currentTime: 0,
689            duration: 0,
690            remaining: 0,
691            videoWidth: 0, // Intrinsic width of the video in pixels.
692            videoHeight: 0, // Intrinsic height of the video in pixels.
693            readyState: 0,
694            networkState: 0,
695            playbackRate: 1, // Warning - Now both an option and a status property
696            ended: 0
697
698            /*		Persistant status properties created dynamically at _init():
699            			width
700            			height
701            			cssClass
702            			nativeVideoControls
703            			noFullWindow
704            			noVolume
705            			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.
706            */
707        },
708
709        internal: { // Instanced in _init()
710            ready: false
711                // instance: undefined
712                // domNode: undefined
713                // htmlDlyCmdId: undefined
714                // autohideId: undefined
715                // mouse: undefined
716                // cmdsIgnored
717        },
718        solution: { // Static Object: Defines the solutions built in jPlayer.
719            html: true,
720            aurora: true,
721            flash: true
722        },
723        // 'MPEG-4 support' : canPlayType('video/mp4; codecs="mp4v.20.8"')
724        format: { // Static Object
725            mp3: {
726                codec: 'audio/mpeg',
727                flashCanPlay: true,
728                media: 'audio'
729            },
730            m4a: { // AAC / MP4
731                codec: 'audio/mp4; codecs="mp4a.40.2"',
732                flashCanPlay: true,
733                media: 'audio'
734            },
735            m3u8a: { // AAC / MP4 / Apple HLS
736                codec: 'application/vnd.apple.mpegurl; codecs="mp4a.40.2"',
737                flashCanPlay: false,
738                media: 'audio'
739            },
740            m3ua: { // M3U
741                codec: 'audio/mpegurl',
742                flashCanPlay: false,
743                media: 'audio'
744            },
745            oga: { // OGG
746                codec: 'audio/ogg; codecs="vorbis, opus"',
747                flashCanPlay: false,
748                media: 'audio'
749            },
750            flac: { // FLAC
751                codec: 'audio/x-flac',
752                flashCanPlay: false,
753                media: 'audio'
754            },
755            wav: { // PCM
756                codec: 'audio/wav; codecs="1"',
757                flashCanPlay: false,
758                media: 'audio'
759            },
760            webma: { // WEBM
761                codec: 'audio/webm; codecs="vorbis"',
762                flashCanPlay: false,
763                media: 'audio'
764            },
765            fla: { // FLV / F4A
766                codec: 'audio/x-flv',
767                flashCanPlay: true,
768                media: 'audio'
769            },
770            rtmpa: { // RTMP AUDIO
771                codec: 'audio/rtmp; codecs="rtmp"',
772                flashCanPlay: true,
773                media: 'audio'
774            },
775            m4v: { // H.264 / MP4
776                codec: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"',
777                flashCanPlay: true,
778                media: 'video'
779            },
780            m3u8v: { // H.264 / AAC / MP4 / Apple HLS
781                codec: 'application/vnd.apple.mpegurl; codecs="avc1.42E01E, mp4a.40.2"',
782                flashCanPlay: false,
783                media: 'video'
784            },
785            m3uv: { // M3U
786                codec: 'audio/mpegurl',
787                flashCanPlay: false,
788                media: 'video'
789            },
790            ogv: { // OGG
791                codec: 'video/ogg; codecs="theora, vorbis"',
792                flashCanPlay: false,
793                media: 'video'
794            },
795            webmv: { // WEBM
796                codec: 'video/webm; codecs="vorbis, vp8"',
797                flashCanPlay: false,
798                media: 'video'
799            },
800            flv: { // FLV / F4V
801                codec: 'video/x-flv',
802                flashCanPlay: true,
803                media: 'video'
804            },
805            rtmpv: { // RTMP VIDEO
806                codec: 'video/rtmp; codecs="rtmp"',
807                flashCanPlay: true,
808                media: 'video'
809            }
810        },
811        _init: function() {
812            var self = this;
813
814            this.element.empty();
815
816            this.status = $.extend({}, this.status); // Copy static to unique instance.
817            this.internal = $.extend({}, this.internal); // Copy static to unique instance.
818
819            // Initialize the time format
820            this.options.timeFormat = $.extend({}, $.jPlayer.timeFormat, this.options.timeFormat);
821
822            // On iOS, assume commands will be ignored before user initiates them.
823            this.internal.cmdsIgnored = $.jPlayer.platform.ipad || $.jPlayer.platform.iphone || $.jPlayer.platform.ipod;
824
825            this.internal.domNode = this.element.get(0);
826
827            // Add key bindings focus to 1st jPlayer instanced with key control enabled.
828            if (this.options.keyEnabled && !$.jPlayer.focus) {
829                $.jPlayer.focus = this;
830            }
831
832            // 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.
833            this.androidFix = {
834                setMedia: false, // True when media set
835                play: false, // True when a progress event will instruct the media to play
836                pause: false, // True when a progress event will instruct the media to pause at a time.
837                time: NaN // The play(time) parameter
838            };
839            if ($.jPlayer.platform.android) {
840                this.options.preload = this.options.preload !== 'auto' ? 'metadata' : 'auto'; // Default to metadata, but allow auto.
841            }
842
843            this.formats = []; // Array based on supplied string option. Order defines priority.
844            this.solutions = []; // Array based on solution string option. Order defines priority.
845            this.require = {}; // Which media types are required: video, audio.
846
847            this.htmlElement = {}; // DOM elements created by jPlayer
848            this.html = {}; // In _init()'s this.desired code and setmedia(): Accessed via this[solution], where solution from this.solutions array.
849            this.html.audio = {};
850            this.html.video = {};
851            this.aurora = {}; // In _init()'s this.desired code and setmedia(): Accessed via this[solution], where solution from this.solutions array.
852            this.aurora.formats = [];
853            this.aurora.properties = [];
854            this.flash = {}; // In _init()'s this.desired code and setmedia(): Accessed via this[solution], where solution from this.solutions array.
855
856            this.css = {};
857            this.css.cs = {}; // Holds the css selector strings
858            this.css.jq = {}; // Holds jQuery selectors. ie., $(css.cs.method)
859
860            this.ancestorJq = []; // Holds jQuery selector of cssSelectorAncestor. Init would use $() instead of [], but it is only 1.4+
861
862            this.options.volume = this._limitValue(this.options.volume, 0, 1); // Limit volume value's bounds.
863
864            // Create the formats array, with prority based on the order of the supplied formats string
865            $.each(this.options.supplied.toLowerCase().split(","), function(index1, value1) {
866                var format = value1.replace(/^\s+|\s+$/g, ""); //trim
867                if (self.format[format]) { // Check format is valid.
868                    var dupFound = false;
869                    $.each(self.formats, function(index2, value2) { // Check for duplicates
870                        if (format === value2) {
871                            dupFound = true;
872                            return false;
873                        }
874                    });
875                    if (!dupFound) {
876                        self.formats.push(format);
877                    }
878                }
879            });
880
881            // Create the solutions array, with prority based on the order of the solution string
882            $.each(this.options.solution.toLowerCase().split(","), function(index1, value1) {
883                var solution = value1.replace(/^\s+|\s+$/g, ""); //trim
884                if (self.solution[solution]) { // Check solution is valid.
885                    var dupFound = false;
886                    $.each(self.solutions, function(index2, value2) { // Check for duplicates
887                        if (solution === value2) {
888                            dupFound = true;
889                            return false;
890                        }
891                    });
892                    if (!dupFound) {
893                        self.solutions.push(solution);
894                    }
895                }
896            });
897
898            // Create Aurora.js formats array
899            $.each(this.options.auroraFormats.toLowerCase().split(","), function(index1, value1) {
900                var format = value1.replace(/^\s+|\s+$/g, ""); //trim
901                if (self.format[format]) { // Check format is valid.
902                    var dupFound = false;
903                    $.each(self.aurora.formats, function(index2, value2) { // Check for duplicates
904                        if (format === value2) {
905                            dupFound = true;
906                            return false;
907                        }
908                    });
909                    if (!dupFound) {
910                        self.aurora.formats.push(format);
911                    }
912                }
913            });
914
915            this.internal.instance = "jp_" + this.count;
916            this.instances[this.internal.instance] = this.element;
917
918            // Check the jPlayer div has an id and create one if required. Important for Flash to know the unique id for comms.
919            if (!this.element.attr("id")) {
920                this.element.attr("id", this.options.idPrefix + "_jplayer_" + this.count);
921            }
922
923            this.internal.self = $.extend({}, {
924                id: this.element.attr("id"),
925                jq: this.element
926            });
927            this.internal.audio = $.extend({}, {
928                id: this.options.idPrefix + "_audio_" + this.count,
929                jq: undefined
930            });
931            this.internal.video = $.extend({}, {
932                id: this.options.idPrefix + "_video_" + this.count,
933                jq: undefined
934            });
935            this.internal.flash = $.extend({}, {
936                id: this.options.idPrefix + "_flash_" + this.count,
937                jq: undefined,
938                swf: this.options.swfPath + (this.options.swfPath.toLowerCase().slice(-4) !== ".swf" ? (this.options.swfPath && this.options.swfPath.slice(-1) !== "/" ? "/" : "") + "jquery.jplayer.swf" : "")
939            });
940            this.internal.poster = $.extend({}, {
941                id: this.options.idPrefix + "_poster_" + this.count,
942                jq: undefined
943            });
944
945            // Register listeners defined in the constructor
946            $.each($.jPlayer.event, function(eventName, eventType) {
947                if (self.options[eventName] !== undefined) {
948                    self.element.bind(eventType + ".jPlayer", self.options[eventName]); // With .jPlayer namespace.
949                    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.
950                }
951            });
952
953            // Determine if we require solutions for audio, video or both media types.
954            this.require.audio = false;
955            this.require.video = false;
956            $.each(this.formats, function(priority, format) {
957                self.require[self.format[format].media] = true;
958            });
959
960            // Now required types are known, finish the options default settings.
961            if (this.require.video) {
962                this.options = $.extend(true, {},
963                    this.optionsVideo,
964                    this.options
965                );
966            } else {
967                this.options = $.extend(true, {},
968                    this.optionsAudio,
969                    this.options
970                );
971            }
972            this._setSize(); // update status and jPlayer element size
973
974            // Determine the status for Blocklisted options.
975            this.status.nativeVideoControls = this._uaBlocklist(this.options.nativeVideoControls);
976            this.status.noFullWindow = this._uaBlocklist(this.options.noFullWindow);
977            this.status.noVolume = this._uaBlocklist(this.options.noVolume);
978
979            // Create event handlers if native fullscreen is supported
980            if ($.jPlayer.nativeFeatures.fullscreen.api.fullscreenEnabled) {
981                this._fullscreenAddEventListeners();
982            }
983
984            // The native controls are only for video and are disabled when audio is also used.
985            this._restrictNativeVideoControls();
986
987            // Create the poster image.
988            this.htmlElement.poster = document.createElement('img');
989            this.htmlElement.poster.id = this.internal.poster.id;
990            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.
991                if (!self.status.video || self.status.waitForPlay) {
992                    self.internal.poster.jq.show();
993                }
994            };
995            this.element.append(this.htmlElement.poster);
996            this.internal.poster.jq = $("#" + this.internal.poster.id);
997            this.internal.poster.jq.css({ 'width': this.status.width, 'height': this.status.height });
998            this.internal.poster.jq.hide();
999            this.internal.poster.jq.bind("click.jPlayer", function() {
1000                self._trigger($.jPlayer.event.click);
1001            });
1002
1003            // Generate the required media elements
1004            this.html.audio.available = false;
1005            if (this.require.audio) { // If a supplied format is audio
1006                this.htmlElement.audio = document.createElement('audio');
1007                this.htmlElement.audio.id = this.internal.audio.id;
1008                this.html.audio.available = !!this.htmlElement.audio.canPlayType && this._testCanPlayType(this.htmlElement.audio); // Test is for IE9 on Win Server 2008.
1009            }
1010            this.html.video.available = false;
1011            if (this.require.video) { // If a supplied format is video
1012                this.htmlElement.video = document.createElement('video');
1013                this.htmlElement.video.id = this.internal.video.id;
1014                this.html.video.available = !!this.htmlElement.video.canPlayType && this._testCanPlayType(this.htmlElement.video); // Test is for IE9 on Win Server 2008.
1015            }
1016
1017            this.flash.available = this._checkForFlash(10.1);
1018
1019            this.html.canPlay = {};
1020            this.aurora.canPlay = {};
1021            this.flash.canPlay = {};
1022            $.each(this.formats, function(priority, format) {
1023                self.html.canPlay[format] = self.html[self.format[format].media].available && "" !== self.htmlElement[self.format[format].media].canPlayType(self.format[format].codec);
1024                self.aurora.canPlay[format] = ($.inArray(format, self.aurora.formats) > -1);
1025                self.flash.canPlay[format] = self.format[format].flashCanPlay && self.flash.available;
1026            });
1027            this.html.desired = false;
1028            this.aurora.desired = false;
1029            this.flash.desired = false;
1030            $.each(this.solutions, function(solutionPriority, solution) {
1031                if (solutionPriority === 0) {
1032                    self[solution].desired = true;
1033                } else {
1034                    var audioCanPlay = false;
1035                    var videoCanPlay = false;
1036                    $.each(self.formats, function(formatPriority, format) {
1037                        if (self[self.solutions[0]].canPlay[format]) { // The other solution can play
1038                            if (self.format[format].media === 'video') {
1039                                videoCanPlay = true;
1040                            } else {
1041                                audioCanPlay = true;
1042                            }
1043                        }
1044                    });
1045                    self[solution].desired = (self.require.audio && !audioCanPlay) || (self.require.video && !videoCanPlay);
1046                }
1047            });
1048            // This is what jPlayer will support, based on solution and supplied.
1049            this.html.support = {};
1050            this.aurora.support = {};
1051            this.flash.support = {};
1052            $.each(this.formats, function(priority, format) {
1053                self.html.support[format] = self.html.canPlay[format] && self.html.desired;
1054                self.aurora.support[format] = self.aurora.canPlay[format] && self.aurora.desired;
1055                self.flash.support[format] = self.flash.canPlay[format] && self.flash.desired;
1056            });
1057            // If jPlayer is supporting any format in a solution, then the solution is used.
1058            this.html.used = false;
1059            this.aurora.used = false;
1060            this.flash.used = false;
1061            $.each(this.solutions, function(solutionPriority, solution) {
1062                $.each(self.formats, function(formatPriority, format) {
1063                    if (self[solution].support[format]) {
1064                        self[solution].used = true;
1065                        return false;
1066                    }
1067                });
1068            });
1069
1070            // Init solution active state and the event gates to false.
1071            this._resetActive();
1072            this._resetGate();
1073
1074            // Set up the css selectors for the control and feedback entities.
1075            this._cssSelectorAncestor(this.options.cssSelectorAncestor);
1076
1077            // If neither html nor aurora nor flash are being used by this browser, then media playback is not possible. Trigger an error event.
1078            if (!(this.html.used || this.aurora.used || this.flash.used)) {
1079                this._error({
1080                    type: $.jPlayer.error.NO_SOLUTION,
1081                    context: "{solution:'" + this.options.solution + "', supplied:'" + this.options.supplied + "'}",
1082                    message: $.jPlayer.errorMsg.NO_SOLUTION,
1083                    hint: $.jPlayer.errorHint.NO_SOLUTION
1084                });
1085                if (this.css.jq.noSolution.length) {
1086                    this.css.jq.noSolution.show();
1087                }
1088            } else {
1089                if (this.css.jq.noSolution.length) {
1090                    this.css.jq.noSolution.hide();
1091                }
1092            }
1093
1094            // Add the flash solution if it is being used.
1095            if (this.flash.used) {
1096                var htmlObj,
1097                    flashVars = 'jQuery=' + encodeURI(this.options.noConflict) + '&id=' + encodeURI(this.internal.self.id) + '&vol=' + this.options.volume + '&muted=' + this.options.muted;
1098
1099                // Code influenced by SWFObject 2.2: http://code.google.com/p/swfobject/
1100                // Non IE browsers have an initial Flash size of 1 by 1 otherwise the wmode affected the Flash ready event. 
1101
1102                if ($.jPlayer.browser.msie && (Number($.jPlayer.browser.version) < 9 || $.jPlayer.browser.documentMode < 9)) {
1103                    var objStr = '<object id="' + this.internal.flash.id + '" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="0" height="0" tabindex="-1"></object>';
1104
1105                    var paramStr = [
1106                        '<param name="movie" value="' + this.internal.flash.swf + '" />',
1107                        '<param name="FlashVars" value="' + flashVars + '" />',
1108                        '<param name="allowScriptAccess" value="always" />',
1109                        '<param name="bgcolor" value="' + this.options.backgroundColor + '" />',
1110                        '<param name="wmode" value="' + this.options.wmode + '" />'
1111                    ];
1112
1113                    htmlObj = document.createElement(objStr);
1114                    for (var i = 0; i < paramStr.length; i++) {
1115                        htmlObj.appendChild(document.createElement(paramStr[i]));
1116                    }
1117                } else {
1118                    var createParam = function(el, n, v) {
1119                        var p = document.createElement("param");
1120                        p.setAttribute("name", n);
1121                        p.setAttribute("value", v);
1122                        el.appendChild(p);
1123                    };
1124
1125                    htmlObj = document.createElement("object");
1126                    htmlObj.setAttribute("id", this.internal.flash.id);
1127                    htmlObj.setAttribute("name", this.internal.flash.id);
1128                    htmlObj.setAttribute("data", this.internal.flash.swf);
1129                    htmlObj.setAttribute("type", "application/x-shockwave-flash");
1130                    htmlObj.setAttribute("width", "1"); // Non-zero
1131                    htmlObj.setAttribute("height", "1"); // Non-zero
1132                    htmlObj.setAttribute("tabindex", "-1");
1133                    createParam(htmlObj, "flashvars", flashVars);
1134                    createParam(htmlObj, "allowscriptaccess", "always");
1135                    createParam(htmlObj, "bgcolor", this.options.backgroundColor);
1136                    createParam(htmlObj, "wmode", this.options.wmode);
1137                }
1138
1139                this.element.append(htmlObj);
1140                this.internal.flash.jq = $(htmlObj);
1141            }
1142
1143            // Setup playbackRate ability before using _addHtmlEventListeners()
1144            if (this.html.used && !this.flash.used) { // If only HTML
1145                // Using the audio element capabilities for playbackRate. ie., Assuming video element is the same.
1146                this.status.playbackRateEnabled = this._testPlaybackRate('audio');
1147            } else {
1148                this.status.playbackRateEnabled = false;
1149            }
1150
1151            this._updatePlaybackRate();
1152
1153            // Add the HTML solution if being used.
1154            if (this.html.used) {
1155
1156                // The HTML Audio handlers
1157                if (this.html.audio.available) {
1158                    this._addHtmlEventListeners(this.htmlElement.audio, this.html.audio);
1159                    this.element.append(this.htmlElement.audio);
1160                    this.internal.audio.jq = $("#" + this.internal.audio.id);
1161                }
1162
1163                // The HTML Video handlers
1164                if (this.html.video.available) {
1165                    this._addHtmlEventListeners(this.htmlElement.video, this.html.video);
1166                    this.element.append(this.htmlElement.video);
1167                    this.internal.video.jq = $("#" + this.internal.video.id);
1168                    if (this.status.nativeVideoControls) {
1169                        this.internal.video.jq.css({ 'width': this.status.width, 'height': this.status.height });
1170                    } else {
1171                        this.internal.video.jq.css({ 'width': '0px', 'height': '0px' }); // Using size 0x0 since a .hide() causes issues in iOS
1172                    }
1173                    this.internal.video.jq.bind("click.jPlayer", function() {
1174                        self._trigger($.jPlayer.event.click);
1175                    });
1176                }
1177            }
1178
1179            // Add the Aurora.js solution if being used.
1180            if (this.aurora.used) {
1181                // Aurora.js player need to be created for each media, see setMedia function.
1182            }
1183
1184            // Create the bridge that emulates the HTML Media element on the jPlayer DIV
1185            if (this.options.emulateHtml) {
1186                this._emulateHtmlBridge();
1187            }
1188
1189            if ((this.html.used || this.aurora.used) && !this.flash.used) { // If only HTML, then emulate flash ready() call after 100ms.
1190                setTimeout(function() {
1191                    self.internal.ready = true;
1192                    self.version.flash = "n/a";
1193                    self._trigger($.jPlayer.event.repeat); // Trigger the repeat event so its handler can initialize itself with the loop option.
1194                    self._trigger($.jPlayer.event.ready);
1195                }, 100);
1196            }
1197
1198            // Initialize the interface components with the options.
1199            this._updateNativeVideoControls();
1200            // The other controls are now setup in _cssSelectorAncestor()
1201            if (this.css.jq.videoPlay.length) {
1202                this.css.jq.videoPlay.hide();
1203            }
1204
1205            $.jPlayer.prototype.count++; // Change static variable via prototype.
1206        },
1207        destroy: function() {
1208            // MJP: The background change remains. Would need to store the original to restore it correctly.
1209            // MJP: The jPlayer element's size change remains.
1210
1211            // Clear the media to reset the GUI and stop any downloads. Streams on some browsers had persited. (Chrome)
1212            this.clearMedia();
1213            // Remove the size/sizeFull cssClass from the cssSelectorAncestor
1214            this._removeUiClass();
1215            // Remove the times from the GUI
1216            if (this.css.jq.currentTime.length) {
1217                this.css.jq.currentTime.text("");
1218            }
1219            if (this.css.jq.duration.length) {
1220                this.css.jq.duration.text("");
1221            }
1222            // Remove any bindings from the interface controls.
1223            $.each(this.css.jq, function(fn, jq) {
1224                // Check selector is valid before trying to execute method.
1225                if (jq.length) {
1226                    jq.unbind(".jPlayer");
1227                }
1228            });
1229            // Remove the click handlers for $.jPlayer.event.click
1230            this.internal.poster.jq.unbind(".jPlayer");
1231            if (this.internal.video.jq) {
1232                this.internal.video.jq.unbind(".jPlayer");
1233            }
1234            // Remove the fullscreen event handlers
1235            this._fullscreenRemoveEventListeners();
1236            // Remove key bindings
1237            if (this === $.jPlayer.focus) {
1238                $.jPlayer.focus = null;
1239            }
1240            // Destroy the HTML bridge.
1241            if (this.options.emulateHtml) {
1242                this._destroyHtmlBridge();
1243            }
1244            this.element.removeData("jPlayer"); // Remove jPlayer data
1245            this.element.unbind(".jPlayer"); // Remove all event handlers created by the jPlayer constructor
1246            this.element.empty(); // Remove the inserted child elements
1247
1248            delete this.instances[this.internal.instance]; // Clear the instance on the static instance object
1249        },
1250        destroyRemoved: function() { // Destroy any instances that have gone away.
1251            var self = this;
1252            $.each(this.instances, function(i, element) {
1253                if (self.element !== element) { // Do not destroy this instance.
1254                    if (!element.data("jPlayer")) { // Check that element is a real jPlayer.
1255                        element.jPlayer("destroy");
1256                        delete self.instances[i];
1257                    }
1258                }
1259            });
1260        },
1261        enable: function() { // Plan to implement
1262            // options.disabled = false
1263        },
1264        disable: function() { // Plan to implement
1265            // options.disabled = true
1266        },
1267        _testCanPlayType: function(elem) {
1268            // IE9 on Win Server 2008 did not implement canPlayType(), but it has the property.
1269            try {
1270                elem.canPlayType(this.format.mp3.codec); // The type is irrelevant.
1271                return true;
1272            } catch (err) {
1273                return false;
1274            }
1275        },
1276        _testPlaybackRate: function(type) {
1277            // type: String 'audio' or 'video'
1278            var el, rate = 0.5;
1279            type = typeof type === 'string' ? type : 'audio';
1280            el = document.createElement(type);
1281            // Wrapping in a try/catch, just in case older HTML5 browsers throw and error.
1282            try {
1283                if ('playbackRate' in el) {
1284                    el.playbackRate = rate;
1285                    return el.playbackRate === rate;
1286                } else {
1287                    return false;
1288                }
1289            } catch (err) {
1290                return false;
1291            }
1292        },
1293        _uaBlocklist: function(list) {
1294            // list : object with properties that are all regular expressions. Property names are irrelevant.
1295            // Returns true if the user agent is matched in list.
1296            var ua = navigator.userAgent.toLowerCase(),
1297                block = false;
1298
1299            $.each(list, function(p, re) {
1300                if (re && re.test(ua)) {
1301                    block = true;
1302                    return false; // exit $.each.
1303                }
1304            });
1305            return block;
1306        },
1307        _restrictNativeVideoControls: function() {
1308            // Fallback to noFullWindow when nativeVideoControls is true and audio media is being used. Affects when both media types are used.
1309            if (this.require.audio) {
1310                if (this.status.nativeVideoControls) {
1311                    this.status.nativeVideoControls = false;
1312                    this.status.noFullWindow = true;
1313                }
1314            }
1315        },
1316        _updateNativeVideoControls: function() {
1317            if (this.html.video.available && this.html.used) {
1318                // Turn the HTML Video controls on/off
1319                this.htmlElement.video.controls = this.status.nativeVideoControls;
1320                // Show/hide the jPlayer GUI.
1321                this._updateAutohide();
1322                // 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.
1323                if (this.status.nativeVideoControls && this.require.video) {
1324                    this.internal.poster.jq.hide();
1325                    this.internal.video.jq.css({ 'width': this.status.width, 'height': this.status.height });
1326                } else if (this.status.waitForPlay && this.status.video) {
1327                    this.internal.poster.jq.show();
1328                    this.internal.video.jq.css({ 'width': '0px', 'height': '0px' });
1329                }
1330            }
1331        },
1332        _addHtmlEventListeners: function(mediaElement, entity) {
1333            var self = this;
1334            mediaElement.preload = this.options.preload;
1335            mediaElement.muted = this.options.muted;
1336            mediaElement.volume = this.options.volume;
1337
1338            if (this.status.playbackRateEnabled) {
1339                mediaElement.defaultPlaybackRate = this.options.defaultPlaybackRate;
1340                mediaElement.playbackRate = this.options.playbackRate;
1341            }
1342
1343            // Create the event listeners
1344            // Only want the active entity to affect jPlayer and bubble events.
1345            // Using entity.gate so that object is referenced and gate property always current
1346
1347            mediaElement.addEventListener("progress", function() {
1348                if (entity.gate) {
1349                    if (self.internal.cmdsIgnored && this.readyState > 0) { // Detect iOS executed the command
1350                        self.internal.cmdsIgnored = false;
1351                    }
1352                    self._getHtmlStatus(mediaElement);
1353                    self._updateInterface();
1354                    self._trigger($.jPlayer.event.progress);
1355                }
1356            }, false);
1357            mediaElement.addEventListener("loadeddata", function() {
1358                if (entity.gate) {
1359                    self.androidFix.setMedia = false; // Disable the fix after the first progress event.
1360                    if (self.androidFix.play) { // Play Android audio - performing the fix.
1361                        self.androidFix.play = false;
1362                        self.play(self.androidFix.time);
1363                    }
1364                    if (self.androidFix.pause) { // Pause Android audio at time - performing the fix.
1365                        self.androidFix.pause = false;
1366                        self.pause(self.androidFix.time);
1367                    }
1368                    self._trigger($.jPlayer.event.loadeddata);
1369                }
1370            }, false);
1371            mediaElement.addEventListener("timeupdate", function() {
1372                if (entity.gate) {
1373                    self._getHtmlStatus(mediaElement);
1374                    self._updateInterface();
1375                    self._trigger($.jPlayer.event.timeupdate);
1376                }
1377            }, false);
1378            mediaElement.addEventListener("durationchange", function() {
1379                if (entity.gate) {
1380                    self._getHtmlStatus(mediaElement);
1381                    self._updateInterface();
1382                    self._trigger($.jPlayer.event.durationchange);
1383                }
1384            }, false);
1385            mediaElement.addEventListener("play", function() {
1386                if (entity.gate) {
1387                    self._updateButtons(true);
1388                    self._html_checkWaitForPlay(); // So the native controls update this variable and puts the hidden interface in the correct state. Affects toggling native controls.
1389                    self._trigger($.jPlayer.event.play);
1390                }
1391            }, false);
1392            mediaElement.addEventListener("playing", function() {
1393                if (entity.gate) {
1394                    self._updateButtons(true);
1395                    self._seeked();
1396                    self._trigger($.jPlayer.event.playing);
1397                }
1398            }, false);
1399            mediaElement.addEventListener("pause", function() {
1400                if (entity.gate) {
1401                    self._updateButtons(false);
1402                    self._trigger($.jPlayer.event.pause);
1403                }
1404            }, false);
1405            mediaElement.addEventListener("waiting", function() {
1406                if (entity.gate) {
1407                    self._seeking();
1408                    self._trigger($.jPlayer.event.waiting);
1409                }
1410            }, false);
1411            mediaElement.addEventListener("seeking", function() {
1412                if (entity.gate) {
1413                    self._seeking();
1414                    self._trigger($.jPlayer.event.seeking);
1415                }
1416            }, false);
1417            mediaElement.addEventListener("seeked", function() {
1418                if (entity.gate) {
1419                    self._seeked();
1420                    self._trigger($.jPlayer.event.seeked);
1421                }
1422            }, false);
1423            mediaElement.addEventListener("volumechange", function() {
1424                if (entity.gate) {
1425                    // Read the values back from the element as the Blackberry PlayBook shares the volume with the physical buttons master volume control.
1426                    // However, when tested 6th July 2011, those buttons do not generate an event. The physical play/pause button does though.
1427                    self.options.volume = mediaElement.volume;
1428                    self.options.muted = mediaElement.muted;
1429                    self._updateMute();
1430                    self._updateVolume();
1431                    self._trigger($.jPlayer.event.volumechange);
1432                }
1433            }, false);
1434            mediaElement.addEventListener("ratechange", function() {
1435                if (entity.gate) {
1436                    self.options.defaultPlaybackRate = mediaElement.defaultPlaybackRate;
1437                    self.options.playbackRate = mediaElement.playbackRate;
1438                    self._updatePlaybackRate();
1439                    self._trigger($.jPlayer.event.ratechange);
1440                }
1441            }, false);
1442            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.
1443                if (entity.gate) {
1444                    self._seeked();
1445                    self._trigger($.jPlayer.event.suspend);
1446                }
1447            }, false);
1448            mediaElement.addEventListener("ended", function() {
1449                if (entity.gate) {
1450                    // Order of the next few commands are important. Change the time and then pause.
1451                    // Solves a bug in Firefox, where issuing pause 1st c
1451auses the media to play from the start. ie., The pause is ignored.
1452                    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.
1453                        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.)
1454                    }
1455                    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.
1456                    self._updateButtons(false);
1457                    self._getHtmlStatus(mediaElement, true); // With override true. Otherwise Chrome leaves progress at full.
1458                    self._updateInterface();
1459                    self._trigger($.jPlayer.event.ended);
1460                }
1461            }, false);
1462            mediaElement.addEventListener("error", function() {
1463                if (entity.gate) {
1464                    self._updateButtons(false);
1465                    self._seeked();
1466                    if (self.status.srcSet) { // Deals with case of clearMedia() causing an error event.
1467                        clearTimeout(self.internal.htmlDlyCmdId); // Clears any delayed commands used in the HTML solution.
1468                        self.status.waitForLoad = true; // Allows the load operation to try again.
1469                        self.status.waitForPlay = true; // Reset since a play was captured.
1470                        if (self.status.video && !self.status.nativeVideoControls) {
1471                            self.internal.video.jq.css({ 'width': '0px', 'height': '0px' });
1472                        }
1473                        if (self._validString(self.status.media.poster) && !self.status.nativeVideoControls) {
1474                            self.internal.poster.jq.show();
1475                        }
1476                        if (self.css.jq.videoPlay.length) {
1477                            self.css.jq.videoPlay.show();
1478                        }
1479                        self._error({
1480                            type: $.jPlayer.error.URL,
1481                            context: self.status.src, // this.src shows absolute urls. Want context to show the url given.
1482                            message: $.jPlayer.errorMsg.URL,
1483                            hint: $.jPlayer.errorHint.URL
1484                        });
1485                    }
1486                }
1487            }, false);
1488            // Create all the other event listeners that bubble up to a jPlayer event from html, without being used by jPlayer.
1489            $.each($.jPlayer.htmlEvent, function(i, eventType) {
1490                mediaElement.addEventListener(this, function() {
1491                    if (entity.gate) {
1492                        self._trigger($.jPlayer.event[eventType]);
1493                    }
1494                }, false);
1495            });
1496        },
1497        _addAuroraEventListeners: function(player, entity) {
1498            var self = this;
1499            //player.preload = this.options.preload;
1500            //player.muted = this.options.muted;
1501            player.volume = this.options.volume * 100;
1502
1503            // Create the event listeners
1504            // Only want the active entity to affect jPlayer and bubble events.
1505            // Using entity.gate so that object is referenced and gate property always current
1506
1507            player.on("progress", function() {
1508                if (entity.gate) {
1509                    if (self.internal.cmdsIgnored && this.readyState > 0) { // Detect iOS executed the command
1510                        self.internal.cmdsIgnored = false;
1511                    }
1512                    self._getAuroraStatus(player);
1513                    self._updateInterface();
1514                    self._trigger($.jPlayer.event.progress);
1515                    // Progress with song duration, we estimate timeupdate need to be triggered too.
1516                    if (player.duration > 0) {
1517                        self._trigger($.jPlayer.event.timeupdate);
1518                    }
1519                }
1520            }, false);
1521            player.on("ready", function() {
1522                if (entity.gate) {
1523                    self._trigger($.jPlayer.event.loadeddata);
1524                }
1525            }, false);
1526            player.on("duration", function() {
1527                if (entity.gate) {
1528                    self._getAuroraStatus(player);
1529                    self._updateInterface();
1530                    self._trigger($.jPlayer.event.durationchange);
1531                }
1532            }, false);
1533            player.on("end", function() {
1534                if (entity.gate) {
1535                    // Order of the next few commands are important. Change the time and then pause.
1536                    self._updateButtons(false);
1537                    self._getAuroraStatus(player, true);
1538                    self._updateInterface();
1539                    self._trigger($.jPlayer.event.ended);
1540                }
1541            }, false);
1542            player.on("error", function() {
1543                if (entity.gate) {
1544                    self._updateButtons(false);
1545                    self._seeked();
1546                    if (self.status.srcSet) { // Deals with case of clearMedia() causing an error event.
1547                        self.status.waitForLoad = true; // Allows the load operation to try again.
1548                        self.status.waitForPlay = true; // Reset since a play was captured.
1549                        if (self.status.video && !self.status.nativeVideoControls) {
1550                            self.internal.video.jq.css({ 'width': '0px', 'height': '0px' });
1551                        }
1552                        if (self._validString(self.status.media.poster) && !self.status.nativeVideoControls) {
1553                            self.internal.poster.jq.show();
1554                        }
1555                        if (self.css.jq.videoPlay.length) {
1556                            self.css.jq.videoPlay.show();
1557                        }
1558                        self._error({
1559                            type: $.jPlayer.error.URL,
1560                            context: self.status.src, // this.src shows absolute urls. Want context to show the url given.
1561                            message: $.jPlayer.errorMsg.URL,
1562                            hint: $.jPlayer.errorHint.URL
1563                        });
1564                    }
1565                }
1566            }, false);
1567        },
1568        _getHtmlStatus: function(media, override) {
1569            var ct = 0,
1570                cpa = 0,
1571                sp = 0,
1572                cpr = 0;
1573
1574            // Fixes the duration bug in iOS, where the durationchange event occurs when media.duration is not always correct.
1575            // Fixes the initial duration bug in BB OS7, where the media.duration is infinity and displays as NaN:NaN due to Date() using inifity.
1576            if (isFinite(media.duration)) {
1577                this.status.duration = media.duration;
1578            }
1579
1580            ct = media.currentTime;
1581            cpa = (this.status.duration > 0) ? 100 * ct / this.status.duration : 0;
1582            if ((typeof media.seekable === "object") && (media.seekable.length > 0)) {
1583                sp = (this.status.duration > 0) ? 100 * media.seekable.end(media.seekable.length - 1) / this.status.duration : 100;
1584                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.
1585            } else {
1586                sp = 100;
1587                cpr = cpa;
1588            }
1589
1590            if (override) {
1591                ct = 0;
1592                cpr = 0;
1593                cpa = 0;
1594            }
1595
1596            this.status.seekPercent = sp;
1597            this.status.currentPercentRelative = cpr;
1598            this.status.currentPercentAbsolute = cpa;
1599            this.status.currentTime = ct;
1600
1601            this.status.remaining = this.status.duration - this.status.currentTime;
1602
1603            this.status.videoWidth = media.videoWidth;
1604            this.status.videoHeight = media.videoHeight;
1605
1606            this.status.readyState = media.readyState;
1607            this.status.networkState = media.networkState;
1608            this.status.playbackRate = media.playbackRate;
1609            this.status.ended = media.ended;
1610        },
1611        _getAuroraStatus: function(player, override) {
1612            var ct = 0,
1613                cpa = 0,
1614                sp = 0,
1615                cpr = 0;
1616
1617            this.status.duration = player.duration / 1000;
1618
1619            ct = player.currentTime / 1000;
1620            cpa = (this.status.duration > 0) ? 100 * ct / this.status.duration : 0;
1621            if (player.buffered > 0) {
1622                sp = (this.status.duration > 0) ? (player.buffered * this.status.duration) / this.status.duration : 100;
1623                cpr = (this.status.duration > 0) ? ct / (player.buffered * this.status.duration) : 0;
1624            } else {
1625                sp = 100;
1626                cpr = cpa;
1627            }
1628
1629            if (override) {
1630                ct = 0;
1631                cpr = 0;
1632                cpa = 0;
1633            }
1634
1635            this.status.seekPercent = sp;
1636            this.status.currentPercentRelative = cpr;
1637            this.status.currentPercentAbsolute = cpa;
1638            this.status.currentTime = ct;
1639
1640            this.status.remaining = this.status.duration - this.status.currentTime;
1641
1642            this.status.readyState = 4; // status.readyState;
1643            this.status.networkState = 0; // status.networkState;
1644            this.status.playbackRate = 1; // status.playbackRate;
1645            this.status.ended = false; // status.ended;
1646        },
1647        _resetStatus: function() {
1648            this.status = $.extend({}, this.status, $.jPlayer.prototype.status); // Maintains the status properties that persist through a reset.
1649        },
1650        _trigger: function(eventType, error, warning) { // eventType always valid as called using $.jPlayer.event.eventType
1651            var event = $.Event(eventType);
1652            event.jPlayer = {};
1653            event.jPlayer.version = $.extend({}, this.version);
1654            event.jPlayer.options = $.extend(true, {}, this.options); // Deep copy
1655            event.jPlayer.status = $.extend(true, {}, this.status); // Deep copy
1656            event.jPlayer.html = $.extend(true, {}, this.html); // Deep copy
1657            event.jPlayer.aurora = $.extend(true, {}, this.aurora); // Deep copy
1658            event.jPlayer.flash = $.extend(true, {}, this.flash); // Deep copy
1659            if (error) {
1660                event.jPlayer.error = $.extend({}, error);
1661            }
1662            if (warning) {
1663                event.jPlayer.warning = $.extend({}, warning);
1664            }
1665            this.element.trigger(event);
1666        },
1667        jPlayerFlashEvent: function(eventType, status) { // Called from Flash
1668            if (eventType === $.jPlayer.event.ready) {
1669                if (!this.internal.ready) {
1670                    this.internal.ready = true;
1671                    this.internal.flash.jq.css({ 'width': '0px', 'height': '0px' });
1671 // Once Flash generates the ready event, minimise to zero as it is not affected by wmode anymore.
1672
1673                    this.version.flash = status.version;
1674                    if (this.version.needFlash !== this.version.flash) {
1675                        this._error({
1676                            type: $.jPlayer.error.VERSION,
1677                            context: this.version.flash,
1678                            message: $.jPlayer.errorMsg.VERSION + this.version.flash,
1679                            hint: $.jPlayer.errorHint.VERSION
1680                        });
1681                    }
1682                    this._trigger($.jPlayer.event.repeat); // Trigger the repeat event so its handler can initialize itself with the loop option.
1683                    this._trigger(eventType);
1684                } else {
1685                    // This condition occurs if the Flash is hidden and then shown again.
1686                    // Firefox also reloads the Flash if the CSS position changes. position:fixed is used for full screen.
1687
1688                    // Only do this if the Flash is the solution being used at the moment. Affects Media players where both solution may be being used.
1689                    if (this.flash.gate) {
1690
1691                        // Send the current status to the Flash now that it is ready (available) again.
1692                        if (this.status.srcSet) {
1693
1694                            // Need to read original status before issuing the setMedia command.
1695                            var currentTime = this.status.currentTime,
1696                                paused = this.status.paused;
1697
1698                            this.setMedia(this.status.media);
1699                            this.volumeWorker(this.options.volume);
1700                            if (currentTime > 0) {
1701                                if (paused) {
1702                                    this.pause(currentTime);
1703                                } else {
1704                                    this.play(currentTime);
1705                                }
1706                            }
1707                        }
1708                        this._trigger($.jPlayer.event.flashreset);
1709                    }
1710                }
1711            }
1712            if (this.flash.gate) {
1713                switch (eventType) {
1714                    case $.jPlayer.event.progress:
1715                        this._getFlashStatus(status);
1716                        this._updateInterface();
1717                        this._trigger(eventType);
1718                        break;
1719                    case $.jPlayer.event.timeupdate:
1720                        this._getFlashStatus(status);
1721                        this._updateInterface();
1722                        this._trigger(eventType);
1723                        break;
1724                    case $.jPlayer.event.play:
1725                        this._seeked();
1726                        this._updateButtons(true);
1727                        this._trigger(eventType);
1728                        break;
1729                    case $.jPlayer.event.pause:
1730                        this._updateButtons(false);
1731                        this._trigger(eventType);
1732                        break;
1733                    case $.jPlayer.event.ended:
1734                        this._updateButtons(false);
1735                        this._trigger(eventType);
1736                        break;
1737                    case $.jPlayer.event.click:
1738                        this._trigger(eventType); // This could be dealt with by the default
1739                        break;
1740                    case $.jPlayer.event.error:
1741                        this.status.waitForLoad = true; // Allows the load operation to try again.
1742                        this.status.waitForPlay = true; // Reset since a play was captured.
1743                        if (this.status.video) {
1744                            this.internal.flash.jq.css({ 'width': '0px', 'height': '0px' });
1745                        }
1746                        if (this._validString(this.status.media.poster)) {
1747                            this.internal.poster.jq.show();
1748                        }
1749                        if (this.css.jq.videoPlay.length && this.status.video) {
1750                            this.css.jq.videoPlay.show();
1751                        }
1752                        if (this.status.video) { // Set up for another try. Execute before error event.
1753                            this._flash_setVideo(this.status.media);
1754                        } else {
1755                            this._flash_setAudio(this.status.media);
1756                        }
1757                        this._updateButtons(false);
1758                        this._error({
1759                            type: $.jPlayer.error.URL,
1760                            context: status.src,
1761                            message: $.jPlayer.errorMsg.URL,
1762                            hint: $.jPlayer.errorHint.URL
1763                        });
1764                        break;
1765                    case $.jPlayer.event.seeking:
1766                        this._seeking();
1767                        this._trigger(eventType);
1768                        break;
1769                    case $.jPlayer.event.seeked:
1770                        this._seeked();
1771                        this._trigger(eventType);
1772                        break;
1773                    case $.jPlayer.event.ready:
1774                        // The ready event is handled outside the switch statement.
1775                        // Captured here otherwise 2 ready events would be generated if the ready event handler used setMedia.
1776                        break;
1777                    default:
1778                        this._trigger(eventType);
1779                }
1780            }
1781            return false;
1782        },
1783        _getFlashStatus: function(status) {
1784            this.status.seekPercent = status.seekPercent;
1785            this.status.currentPercentRelative = status.currentPercentRelative;
1786            this.status.currentPercentAbsolute = status.currentPercentAbsolute;
1787            this.status.currentTime = status.currentTime;
1788            this.status.duration = status.duration;
1789            this.status.remaining = status.duration - status.currentTime;
1790
1791            this.status.videoWidth = status.videoWidth;
1792            this.status.videoHeight = status.videoHeight;
1793
1794            // The Flash does not generate this information in this release
1795            this.status.readyState = 4; // status.readyState;
1796            this.status.networkState = 0; // status.networkState;
1797            this.status.playbackRate = 1; // status.playbackRate;
1798            this.status.ended = false; // status.ended;
1799        },
1800        _updateButtons: function(playing) {
1801            if (playing === undefined) {
1802                playing = !this.status.paused;
1803            } else {
1804                this.status.paused = !playing;
1805            }
1806            // Apply the state classes. (For the useStateClassSkin:true option)
1807            if (playing) {
1808                this.addStateClass('playing');
1809            } else {
1810                this.removeStateClass('playing');
1811            }
1812            if (!this.status.noFullWindow && this.options.fullWindow) {
1813                this.addStateClass('fullScreen');
1814            } else {
1815                this.removeStateClass('fullScreen');
1816            }
1817            if (this.options.loop) {
1818                this.addStateClass('looped');
1819            } else {
1820                this.removeStateClass('looped');
1821            }
1822            // Toggle the GUI element pairs. (For the useStateClassSkin:false option)
1823            if (this.css.jq.play.length && this.css.jq.pause.length) {
1824                if (playing) {
1825                    this.css.jq.play.hide();
1826                    this.css.jq.pause.show();
1827                } else {
1828                    this.css.jq.play.show();
1829                    this.css.jq.pause.hide();
1830                }
1831            }
1832            if (this.css.jq.restoreScreen.length && this.css.jq.fullScreen.length) {
1833                if (this.status.noFullWindow) {
1834                    this.css.jq.fullScreen.hide();
1835                    this.css.jq.restoreScreen.hide();
1836                } else if (this.options.fullWindow) {
1837                    this.css.jq.fullScreen.hide();
1838                    this.css.jq.restoreScreen.show();
1839                } else {
1840                    this.css.jq.fullScreen.show();
1841                    this.css.jq.restoreScreen.hide();
1842                }
1843            }
1844            if (this.css.jq.repeat.length && this.css.jq.repeatOff.length) {
1845                if (this.options.loop) {
1846                    this.css.jq.repeat.hide();
1847                    this.css.jq.repeatOff.show();
1848                } else {
1849                    this.css.jq.repeat.show();
1850                    this.css.jq.repeatOff.hide();
1851                }
1852            }
1853        },
1854        _updateInterface: function() {
1855            if (this.css.jq.seekBar.length) {
1856                this.css.jq.seekBar.width(this.status.seekPercent + "%");
1857            }
1858            if (this.css.jq.playBar.length) {
1859                if (this.options.smoothPlayBar) {
1860                    this.css.jq.playBar.stop().animate({
1861                        width: this.status.currentPercentAbsolute + "%"
1862                    }, 250, "linear");
1863                } else {
1864                    this.css.jq.playBar.width(this.status.currentPercentRelative + "%");
1865                }
1866            }
1867            var currentTimeText = '';
1868            if (this.css.jq.currentTime.length) {
1869                currentTimeText = this._convertTime(this.status.currentTime);
1870                if (currentTimeText !== this.css.jq.currentTime.text()) {
1871                    this.css.jq.currentTime.text(this._convertTime(this.status.currentTime));
1872                }
1873            }
1874            var durationText = '',
1875                duration = this.status.duration,
1876                remaining = this.status.remaining;
1877            if (this.css.jq.duration.length) {
1878                if (typeof this.status.media.duration === 'string') {
1879                    durationText = this.status.media.duration;
1880                } else {
1881                    if (typeof this.status.media.duration === 'number') {
1882                        duration = this.status.media.duration;
1883                        remaining = duration - this.status.currentTime;
1884                    }
1885                    if (this.options.remainingDuration) {
1886                        durationText = (remaining > 0 ? '-' : '') + this._convertTime(remaining);
1887                    } else {
1888                        durationText = this._convertTime(duration);
1889                    }
1890                }
1891                if (durationText !== this.css.jq.duration.text()) {
1892                    this.css.jq.duration.text(durationText);
1893                }
1894            }
1895        },
1896        _convertTime: ConvertTime.prototype.time,
1897        _seeking: function() {
1898            if (this.css.jq.seekBar.length) {
1899                this.css.jq.seekBar.addClass("jp-seeking-bg");
1900            }
1901            this.addStateClass('seeking');
1902        },
1903        _seeked: function() {
1904            if (this.css.jq.seekBar.length) {
1905                this.css.jq.seekBar.removeClass("jp-seeking-bg");
1906            }
1907            this.removeStateClass('seeking');
1908        },
1909        _resetGate: function() {
1910            this.html.audio.gate = false;
1911            this.html.video.gate = false;
1912            this.aurora.gate = false;
1913            this.flash.gate = false;
1914        },
1915        _resetActive: function() {
1916            this.html.active = false;
1917            this.aurora.active = false;
1918            this.flash.active = false;
1919        },
1920        _escapeHtml: function(s) {
1921            return s.split('&').join('&amp;').split('<').join('&lt;').split('>').join('&gt;').split('"').join('&quot;');
1922        },
1923        _qualifyURL: function(url) {
1924            var el = document.createElement('div');
1925            el.innerHTML = '<a href="' + this._escapeHtml(url) + '">x</a>';
1926            return el.firstChild.href;
1927        },
1928        _absoluteMediaUrls: function(media) {
1929            var self = this;
1930            $.each(media, function(type, url) {
1931                if (url && self.format[type] && url.substr(0, 5) !== "data:") {
1932                    media[type] = self._qualifyURL(url);
1933                }
1934            });
1935            return media;
1936        },
1937        addStateClass: function(state) {
1938            if (this.ancestorJq.length) {
1939                this.ancestorJq.addClass(this.options.stateClass[state]);
1940            }
1941        },
1942        removeStateClass: function(state) {
1943            if (this.ancestorJq.length) {
1944                this.ancestorJq.removeClass(this.options.stateClass[state]);
1945            }
1946        },
1947        setMedia: function(media) {
1948
1949            /*	media[format] = String: URL of format. Must contain all of the supplied option's video or audio formats.
1950             *	media.poster = String: Video poster URL.
1951             *	media.track = Array: Of objects defining the track element: kind, src, srclang, label, def.
1952             *	media.stream = Boolean: * NOT IMPLEMENTED * Designating actual media streams. ie., "false/undefined" for files. Plan to refresh the flash every so often.
1953             */
1954
1955            var self = this,
1956                supported = false,
1957                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.
1958
1959            this._resetMedia();
1960            this._resetGate();
1961            this._resetActive();
1962
1963            // Clear the Android Fix.
1964            this.androidFix.setMedia = false;
1965            this.androidFix.play = false;
1966            this.androidFix.pause = false;
1967
1968            // Convert all media URLs to absolute URLs.
1969            media = this._absoluteMediaUrls(media);
1970
1971            $.each(this.formats, function(formatPriority, format) {
1972                var isVideo = self.format[format].media === 'video';
1973                $.each(self.solutions, function(solutionPriority, solution) {
1974                    if (self[solution].support[format] && self._validString(media[format])) { // Format supported in solution and url given for format.
1975                        var isHtml = solution === 'html';
1976                        var isAurora = solution === 'aurora';
1977
1978                        if (isVideo) {
1979                            if (isHtml) {
1980                                self.html.video.gate = true;
1981                                self._html_setVideo(media);
1982                                self.html.active = true;
1983                            } else {
1984                                self.flash.gate = true;
1985                                self._flash_setVideo(media);
1986                                self.flash.active = true;
1987                            }
1988                            if (self.css.jq.videoPlay.length) {
1989                                self.css.jq.videoPlay.show();
1990                            }
1991                            self.status.video = true;
1992                        } else {
1993                            if (isHtml) {
1994                                self.html.audio.gate = true;
1995                                self._html_setAudio(media);
1996                                self.html.active = true;
1997
1998                                // Setup the Android Fix - Only for HTML audio.
1999                                if ($.jPlayer.platform.android) {
2000                                    self.androidFix.setMedia = true;
2001                                }
2002                            } else if (isAurora) {
2003                                self.aurora.gate = true;
2004                                self._aurora_setAudio(media);
2005                                self.aurora.active = true;
2006                            } else {
2007                                self.flash.gate = true;
2008                                self._flash_setAudio(media);
2009                                self.flash.active = true;
2010                            }
2011                            if (self.css.jq.videoPlay.length) {
2012                                self.css.jq.videoPlay.hide();
2013                            }
2014                            self.status.video = false;
2015                        }
2016
2017                        supported = true;
2018                        return false; // Exit $.each
2019                    }
2020                });
2021                if (supported) {
2022                    return false; // Exit $.each
2023                }
2024            });
2025
2026            if (supported) {
2027                if (!(this.status.nativeVideoControls && this.html.video.gate)) {
2028                    // Set poster IMG if native video controls are not being used
2029                    // Note: With IE the IMG onload event occurs immediately when cached.
2030                    // Note: Poster hidden by default in _resetMedia()
2031                    if (this._validString(media.poster)) {
2032                        if (posterChanged) { // Since some browsers do not generate img onload event.
2033                            this.htmlElement.poster.src = media.poster;
2034                        } else {
2035                            this.internal.poster.jq.show();
2036                        }
2037                    }
2038                }
2039                if (typeof media.title === 'string') {
2040                    if (this.css.jq.title.length) {
2041                        this.css.jq.title.html(media.title);
2042                    }
2043                    if (this.htmlElement.audio) {
2044                        this.htmlElement.audio.setAttribute('title', media.title);
2045                    }
2046                    if (this.htmlElement.video) {
2047                        this.htmlElement.video.setAttribute('title', media.title);
2048                    }
2049                }
2050                this.status.srcSet = true;
2051                this.status.media = $.extend({}, media);
2052                this._updateButtons(false);
2053                this._updateInterface();
2054                this._trigger($.jPlayer.event.setmedia);
2055            } else { // jPlayer cannot support any formats provided in this browser
2056                // Send an error event
2057                this._error({
2058                    type: $.jPlayer.error.NO_SUPPORT,
2059                    context: "{supplied:'" + this.options.supplied + "'}",
2060                    message: $.jPlayer.errorMsg.NO_SUPPORT,
2061                    hint: $.jPlayer.errorHint.NO_SUPPORT
2062                });
2063            }
2064        },
2065        _resetMedia: function() {
2066            this._resetStatus();
2067            this._updateButtons(false);
2068            this._updateInterface();
2069            this._seeked();
2070            this.internal.poster.jq.hide();
2071
2072            clearTimeout(this.internal.htmlDlyCmdId);
2073
2074            if (this.html.active) {
2075                this._html_resetMedia();
2076            } else if (this.aurora.active) {
2077                this._aurora_resetMedia();
2078            } else if (this.flash.active) {
2079                this._flash_resetMedia();
2080            }
2081        },
2082        clearMedia: function() {
2083            this._resetMedia();
2084
2085            if (this.html.active) {
2086                this._html_clearMedia();
2087            } else if (this.aurora.active) {
2088                this._aurora_clearMedia();
2089            } else if (this.flash.active) {
2090                this._flash_clearMedia();
2091            }
2092
2093            this._resetGate();
2094            this._resetActive();
2095        },
2096        load: function() {
2097            if (this.status.srcSet) {
2098                if (this.html.active) {
2099                    this._html_load();
2100                } else if (this.aurora.active) {
2101                    this._aurora_load();
2102                } else if (this.flash.active) {
2103                    this._flash_load();
2104                }
2105            } else {
2106                this._urlNotSetError("load");
2107            }
2108        },
2109        focus: function() {
2110            if (this.options.keyEnabled) {
2111                $.jPlayer.focus = this;
2112            }
2113        },
2114        play: function(time) {
2115            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.
2116            if (guiAction && this.options.useStateClassSkin && !this.status.paused) {
2117                this.pause(time); // The time would be the click event, but passing it over so info is not lost.
2118            } else {
2119                time = (typeof time === "number") ? time : NaN; // Remove jQuery event from click handler
2120                if (this.status.srcSet) {
2121                    this.focus();
2122                    if (this.html.active) {
2123                        this._html_play(time);
2124                    } else if (this.aurora.active) {
2125                        this._aurora_play(time);
2126                    } else if (this.flash.active) {
2127                        this._flash_play(time);
2128                    }
2129                } else {
2130                    this._urlNotSetError("play");
2131                }
2132            }
2133        },
2134        videoPlay: function() { // Handles clicks on the play button over the video poster
2135            this.play();
2136        },
2137        pause: function(time) {
2138            time = (typeof time === "number") ? time : NaN; // Remove jQuery event from click handler
2139            if (this.status.srcSet) {
2140                if (this.html.active) {
2141                    this._html_pause(time);
2142                } else if (this.aurora.active) {
2143                    this._aurora_pause(time);
2144                } else if (this.flash.active) {
2145                    this._flash_pause(time);
2146                }
2147            } else {
2148                this._urlNotSetError("pause");
2149            }
2150        },
2151        tellOthers: function(command, conditions) {
2152            var self = this,
2153                hasConditions = typeof conditions === 'function',
2154                args = Array.prototype.slice.call(arguments); // Convert arguments to an Array.
2155
2156            if (typeof command !== 'string') { // Ignore, since no command.
2157                return; // Return undefined to maintain chaining.
2158            }
2159            if (hasConditions) {
2160                args.splice(1, 1); // Remove the conditions from the arguments
2161            }
2162
2163            $.jPlayer.prototype.destroyRemoved();
2164            $.each(this.instances, function() {
2165                // Remember that "this" is the instance's "element" in the $.each() loop.
2166                if (self.element !== this) { // Do not tell my instance.
2167                    if (!hasConditions || conditions.call(this.data("jPlayer"), self)) {
2168                        this.jPlayer.apply(this, args);
2169                    }
2170                }
2171            });
2172        },
2173        pauseOthers: function(time) {
2174            this.tellOthers("pause", function() {
2175                // In the conditions function, the "this" context is the other instance's jPlayer object.
2176                return this.status.srcSet;
2177            }, time);
2178        },
2179        stop: function() {
2180            if (this.status.srcSet) {
2181                if (this.html.active) {
2182                    this._html_pause(0);
2183                } else if (this.aurora.active) {
2184                    this._aurora_pause(0);
2185                } else if (this.flash.active) {
2186                    this._flash_pause(0);
2187                }
2188            } else {
2189                this._urlNotSetError("stop");
2190            }
2191        },
2192        playHead: function(p) {
2193            p = this._limitValue(p, 0, 100);
2194            if (this.status.srcSet) {
2195                if (this.html.active) {
2196                    this._html_playHead(p);
2197                } else if (this.aurora.active) {
2198                    this._aurora_playHead(p);
2199                } else if (this.flash.active) {
2200                    this._flash_playHead(p);
2201                }
2202            } else {
2203                this._urlNotSetError("playHead");
2204            }
2205        },
2206        _muted: function(muted) {
2207            this.mutedWorker(muted);
2208            if (this.options.globalVolume) {
2209                this.tellOthers("mutedWorker", function() {
2210                    // Check the other instance has global volume enabled.
2211                    return this.options.globalVolume;
2212                }, muted);
2213            }
2214        },
2215        mutedWorker: function(muted) {
2216            this.options.muted = muted;
2217            if (this.html.used) {
2218                this._html_setProperty('muted', muted);
2219            }
2220            if (this.aurora.used) {
2221                this._aurora_mute(muted);
2222            }
2223            if (this.flash.used) {
2224                this._flash_mute(muted);
2225            }
2226
2227            // The HTML solution generates this event from the media element itself.
2228            if (!this.html.video.gate && !this.html.audio.gate) {
2229                this._updateMute(muted);
2230                this._updateVolume(this.options.volume);
2231                this._trigger($.jPlayer.event.volumechange);
2232            }
2233        },
2234        mute: function(mute) { // mute is either: undefined (true), an event object (true) or a boolean (muted).
2235            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.
2236            if (guiAction && this.options.useStateClassSkin && this.options.muted) {
2237                this._muted(false);
2238            } else {
2239                mute = mute === undefined ? true : !!mute;
2240                this._muted(mute);
2241            }
2242        },
2243        unmute: function(unmute) { // unmute is either: undefined (true), an event object (true) or a boolean (!muted).
2244            unmute = unmute === undefined ? true : !!unmute;
2245            this._muted(!unmute);
2246        },
2247        _updateMute: function(mute) {
2248            if (mute === undefined) {
2249                mute = this.options.muted;
2250            }
2251            if (mute) {
2252                this.addStateClass('muted');
2253            } else {
2254                this.removeStateClass('muted');
2255            }
2256            if (this.css.jq.mute.length && this.css.jq.unmute.length) {
2257                if (this.status.noVolume) {
2258                    this.css.jq.mute.hide();
2259                    this.css.jq.unmute.hide();
2260                } else if (mute) {
2261                    this.css.jq.mute.hide();
2262                    this.css.jq.unmute.show();
2263                } else {
2264                    this.css.jq.mute.show();
2265                    this.css.jq.unmute.hide();
2266                }
2267            }
2268        },
2269        volume: function(v) {
2270            this.volumeWorker(v);
2271            if (this.options.globalVolume) {
2272                this.tellOthers("volumeWorker", function() {
2273                    // Check the other instance has global volume enabled.
2274                    return this.options.globalVolume;
2275                }, v);
2276            }
2277        },
2278        volumeWorker: function(v) {
2279            v = this._limitValue(v, 0, 1);
2280            this.options.volume = v;
2281
2282            if (this.html.used) {
2283                this._html_setProperty('volume', v);
2284            }
2285            if (this.aurora.used) {
2286                this._aurora_volume(v);
2287            }
2288            if (this.flash.used) {
2289                this._flash_volume(v);
2290            }
2291
2292            // The HTML solution generates this event from the media element itself.
2293            if (!this.html.video.gate && !this.html.audio.gate) {
2294                this._updateVolume(v);
2295                this._trigger($.jPlayer.event.volumechange);
2296            }
2297        },
2298        volumeBar: function(e) { // Handles clicks on the volumeBar
2299            if (this.css.jq.volumeBar.length) {
2300                // Using $(e.currentTarget) to enable multiple volume bars
2301                var $bar = $(e.currentTarget),
2302                    offset = $bar.offset(),
2303                    x = e.pageX - offset.left,
2304                    w = $bar.width(),
2305                    y = $bar.height() - e.pageY + offset.top,
2306                    h = $bar.height();
2307                if (this.options.verticalVolume) {
2308                    this.volume(y / h);
2309                } else {
2310                    this.volume(x / w);
2311                }
2312            }
2313            if (this.options.muted) {
2314                this._muted(false);
2315            }
2316        },
2317        _updateVolume: function(v) {
2318            if (v === undefined) {
2319                v = this.options.volume;
2320            }
2321            v = this.options.muted ? 0 : v;
2322
2323            if (this.status.noVolume) {
2324                this.addStateClass('noVolume');
2325                if (this.css.jq.volumeBar.length) {
2326                    this.css.jq.volumeBar.hide();
2327                }
2328                if (this.css.jq.volumeBarValue.length) {
2329                    this.css.jq.volumeBarValue.hide();
2330                }
2331                if (this.css.jq.volumeMax.length) {
2332                    this.css.jq.volumeMax.hide();
2333                }
2334            } else {
2335                this.removeStateClass('noVolume');
2336                if (this.css.jq.volumeBar.length) {
2337                    this.css.jq.volumeBar.show();
2338                }
2339                if (this.css.jq.volumeBarValue.length) {
2340                    this.css.jq.volumeBarValue.show();
2341                    this.css.jq.volumeBarValue[this.options.verticalVolume ? "height" : "width"]((v * 100) + "%");
2342                }
2343                if (this.css.jq.volumeMax.length) {
2344                    this.css.jq.volumeMax.show();
2345                }
2346            }
2347        },
2348        volumeMax: function() { // Handles clicks on the volume max
2349            this.volume(1);
2350            if (this.options.muted) {
2351                this._muted(false);
2352            }
2353        },
2354        _cssSelectorAncestor: function(ancestor) {
2355            var self = this;
2356            this.options.cssSelectorAncestor = ancestor;
2357            this._removeUiClass();
2358            this.ancestorJq = ancestor ? $(ancestor) : []; // Would use $() instead of [], but it is only 1.4+
2359            if (ancestor && this.ancestorJq.length !== 1) { // So empty strings do not generate the warning.
2360                this._warning({
2361                    type: $.jPlayer.warning.CSS_SELECTOR_COUNT,
2362                    context: ancestor,
2363                    message: $.jPlayer.warningMsg.CSS_SELECTOR_COUNT + this.ancestorJq.length + " found for cssSelectorAncestor.",
2364                    hint: $.jPlayer.warningHint.CSS_SELECTOR_COUNT
2365                });
2366            }
2367            this._addUiClass();
2368            $.each(this.options.cssSelector, function(fn, cssSel) {
2369                self._cssSelector(fn, cssSel);
2370            });
2371
2372            // Set the GUI to the current state.
2373            this._updateInterface();
2374            this._updateButtons();
2375            this._updateAutohide();
2376            this._updateVolume();
2377            this._updateMute();
2378        },
2379        _cssSelector: function(fn, cssSel) {
2380            var self = this;
2381            if (typeof cssSel === 'string') {
2382                if ($.jPlayer.prototype.options.cssSelector[fn]) {
2383                    if (this.css.jq[fn] && this.css.jq[fn].length) {
2384                        this.css.jq[fn].unbind(".jPlayer");
2385                    }
2386                    this.options.cssSelector[fn] = cssSel;
2387                    this.css.cs[fn] = this.options.cssSelectorAncestor + " " + cssSel;
2388
2389                    if (cssSel) { // Checks for empty string
2390                        this.css.jq[fn] = $(this.css.cs[fn]);
2391                    } else {
2392                        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. 
2393                    }
2394
2395                    if (this.css.jq[fn].length && this[fn]) {
2396                        var handler = function(e) {
2397                            e.preventDefault();
2398                            self[fn](e);
2399                            if (self.options.autoBlur) {
2400                                $(this).blur();
2401                            } else {
2402                                $(this).focus(); // Force focus for ARIA.
2403                            }
2404                        };
2405                        this.css.jq[fn].bind("click.jPlayer", handler); // Using jPlayer namespace
2406                    }
2407
2408                    if (cssSel && this.css.jq[fn].length !== 1) { // So empty strings do not generate the warning. ie., they just remove the old one.
2409                        this._warning({
2410                            type: $.jPlayer.warning.CSS_SELECTOR_COUNT,
2411                            context: this.css.cs[fn],
2412                            message: $.jPlayer.warningMsg.CSS_SELECTOR_COUNT + this.css.jq[fn].length + " found for " + fn + " method.",
2413                            hint: $.jPlayer.warningHint.CSS_SELECTOR_COUNT
2414                        });
2415                    }
2416                } else {
2417                    this._warning({
2418                        type: $.jPlayer.warning.CSS_SELECTOR_METHOD,
2419                        context: fn,
2420                        message: $.jPlayer.warningMsg.CSS_SELECTOR_METHOD,
2421                        hint: $.jPlayer.warningHint.CSS_SELECTOR_METHOD
2422                    });
2423                }
2424            } else {
2425                this._warning({
2426                    type: $.jPlayer.warning.CSS_SELECTOR_STRING,
2427                    context: cssSel,
2428                    message: $.jPlayer.warningMsg.CSS_SELECTOR_STRING,
2429                    hint: $.jPlayer.warningHint.CSS_SELECTOR_STRING
2430                });
2431            }
2432        },
2433        duration: function(e) {
2434            if (this.options.toggleDuration) {
2435                if (this.options.captureDuration) {
2436                    e.stopPropagation();
2437                }
2438                this._setOption("remainingDuration", !this.options.remainingDuration);
2439            }
2440        },
2441        seekBar: function(e) { // Handles clicks on the seekBar
2442            if (this.css.jq.seekBar.length) {
2443                // Using $(e.currentTarget) to enable multiple seek bars
2444                var $bar = $(e.currentTarget),
2445                    offset = $bar.offset(),
2446                    x = e.pageX - offset.left,
2447                    w = $bar.width(),
2448                    p = 100 * x / w;
2449                this.playHead(p);
2450            }
2451        },
2452        playbackRate: function(pbr) {
2453            this._setOption("playbackRate", pbr);
2454        },
2455        playbackRateBar: function(e) { // Handles clicks on the playbackRateBar
2456            if (this.css.jq.playbackRateBar.length) {
2457                // Using $(e.currentTarget) to enable multiple playbackRate bars
2458                var $bar = $(e.currentTarget),
2459                    offset = $bar.offset(),
2460                    x = e.pageX - offset.left,
2461                    w = $bar.width(),
2462                    y = $bar.height() - e.pageY + offset.top,
2463                    h = $bar.height(),
2464                    ratio, pbr;
2465                if (this.options.verticalPlaybackRate) {
2466                    ratio = y / h;
2467                } else {
2468                    ratio = x / w;
2469                }
2470                pbr = ratio * (this.options.maxPlaybackRate - this.options.minPlaybackRate) + this.options.minPlaybackRate;
2471                this.playbackRate(pbr);
2472            }
2473        },
2474        _updatePlaybackRate: function() {
2475            var pbr = this.options.playbackRate,
2476                ratio = (pbr - this.options.minPlaybackRate) / (this.options.maxPlaybackRate - this.options.minPlaybackRate);
2477            if (this.status.playbackRateEnabled) {
2478                if (this.css.jq.playbackRateBar.length) {
2479                    this.css.jq.playbackRateBar.show();
2480                }
2481                if (this.css.jq.playbackRateBarValue.length) {
2482                    this.css.jq.playbackRateBarValue.show();
2483                    this.css.jq.playbackRateBarValue[this.options.verticalPlaybackRate ? "height" : "width"]((ratio * 100) + "%");
2484                }
2485            } else {
2486                if (this.css.jq.playbackRateBar.length) {
2487                    this.css.jq.playbackRateBar.hide();
2488                }
2489                if (this.css.jq.playbackRateBarValue.length) {
2490                    this.css.jq.playbackRateBarValue.hide();
2491                }
2492            }
2493        },
2494        repeat: function(event) { // Handle clicks on the repeat button
2495            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.
2496            if (guiAction && this.options.useStateClassSkin && this.options.loop) {
2497                this._loop(false);
2498            } else {
2499                this._loop(true);
2500            }
2501        },
2502        repeatOff: function() { // Handle clicks on the repeatOff button
2503            this._loop(false);
2504        },
2505        _loop: function(loop) {
2506            if (this.options.loop !== loop) {
2507                this.options.loop = loop;
2508                this._updateButtons();
2509                this._trigger($.jPlayer.event.repeat);
2510            }
2511        },
2512
2513        // 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.
2514        option: function(key, value) {
2515            var options = key;
2516
2517            // Enables use: options().  Returns a copy of options object
2518            if (arguments.length === 0) {
2519                return $.extend(true, {}, this.options);
2520            }
2521
2522            if (typeof key === "string") {
2523                var keys = key.split(".");
2524
2525                // Enables use: options("someOption")  Returns a copy of the option. Supports dot notation.
2526                if (value === undefined) {
2527
2528                    var opt = $.extend(true, {}, this.options);
2529                    for (var i = 0; i < keys.length; i++) {
2530                        if (opt[keys[i]] !== undefined) {
2531                            opt = opt[keys[i]];
2532                        } else {
2533                            this._warning({
2534                                type: $.jPlayer.warning.OPTION_KEY,
2535                                context: key,
2536                                message: $.jPlayer.warningMsg.OPTION_KEY,
2537                                hint: $.jPlayer.warningHint.OPTION_KEY
2538                            });
2539                            return undefined;
2540                        }
2541                    }
2542                    return opt;
2543                }
2544
2545                // Enables use: options("someOptionObject", someObject}).  Creates: {someOptionObject:someObject}
2546                // Enables use: options("someOption", someValue).  Creates: {someOption:someValue}
2547                // Enables use: options("someOptionObject.someOption", someValue).  Creates: {someOptionObject:{someOption:someValue}}
2548
2549                options = {};
2550                var opts = options;
2551
2552                for (var j = 0; j < keys.length; j++) {
2553                    if (j < keys.length - 1) {
2554                        opts[keys[j]] = {};
2555                        opts = opts[keys[j]];
2556                    } else {
2557                        opts[keys[j]] = value;
2558                    }
2559                }
2560            }
2561
2562            // Otherwise enables use: options(optionObject).  Uses original object (the key)
2563
2564            this._setOptions(options);
2565
2566            return this;
2567        },
2568        _setOptions: function(options) {
2569            var self = this;
2570            $.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.
2571                self._setOption(key, value);
2572            });
2573
2574            return this;
2575        },
2576        _setOption: function(key, value) {
2577            var self = this;
2578
2579            // The ability to set options is limited at this time.
2580
2581            switch (key) {
2582                case "volume":
2583                    this.volume(value);
2584                    break;
2585                case "muted":
2586                    this._muted(value);
2587                    break;
2588                case "globalVolume":
2589                    this.options[key] = value;
2590                    break;
2591                case "cssSelectorAncestor":
2592                    this._cssSelectorAncestor(value); // Set and refresh all associations for the new ancestor.
2593                    break;
2594                case "cssSelector":
2595                    $.each(value, function(fn, cssSel) {
2596                        self._cssSelector(fn, cssSel); // NB: The option is set inside this function, after further validity checks.
2597                    });
2598                    break;
2599                case "playbackRate":
2600                    this.options[key] = value = this._limitValue(value, this.options.minPlaybackRate, this.options.maxPlaybackRate);
2601                    if (this.html.used) {
2602                        this._html_setProperty('playbackRate', value);
2603                    }
2604                    this._updatePlaybackRate();
2605                    break;
2606                case "defaultPlaybackRate":
2607                    this.options[key] = value = this._limitValue(value, this.options.minPlaybackRate, this.options.maxPlaybackRate);
2608                    if (this.html.used) {
2609                        this._html_setProperty('defaultPlaybackRate', value);
2610                    }
2611                    this._updatePlaybackRate();
2612                    break;
2613                case "minPlaybackRate":
2614                    this.options[key] = value = this._limitValue(value, 0.1, this.options.maxPlaybackRate - 0.1);
2615                    this._updatePlaybackRate();
2616                    break;
2617                case "maxPlaybackRate":
2618                    this.options[key] = value = this._limitValue(value, this.options.minPlaybackRate + 0.1, 16);
2619                    this._updatePlaybackRate();
2620                    break;
2621                case "fullScreen":
2622                    if (this.options[key] !== value) { // if changed
2623                        var wkv = $.jPlayer.nativeFeatures.fullscreen.used.webkitVideo;
2624                        if (!wkv || wkv && !this.status.waitForPlay) {
2625                            if (!wkv) { // No sensible way to unset option on these devices.
2626                                this.options[key] = value;
2627                            }
2628                            if (value) {
2629                                this._requestFullscreen();
2630                            } else {
2631                                this._exitFullscreen();
2632                            }
2633                            if (!wkv) {
2634                                this._setOption("fullWindow", value);
2635                            }
2636                        }
2637                    }
2638                    break;
2639                case "fullWindow":
2640                    if (this.options[key] !== value) { // if changed
2641                        this._removeUiClass();
2642                        this.options[key] = value;
2643                        this._refreshSize();
2644                    }
2645                    break;
2646                case "size":
2647                    if (!this.options.fullWindow && this.options[key].cssClass !== value.cssClass) {
2648                        this._removeUiClass();
2649                    }
2650                    this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed.
2651                    this._refreshSize();
2652                    break;
2653                case "sizeFull":
2654                    if (this.options.fullWindow && this.options[key].cssClass !== value.cssClass) {
2655                        this._removeUiClass();
2656                    }
2657                    this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed.
2658                    this._refreshSize();
2659                    break;
2660                case "autohide":
2661                    this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed.
2662                    this._updateAutohide();
2663                    break;
2664                case "loop":
2665                    this._loop(value);
2666                    break;
2667                case "remainingDuration":
2668                    this.options[key] = value;
2669                    this._updateInterface();
2670                    break;
2671                case "toggleDuration":
2672                    this.options[key] = value;
2673                    break;
2674                case "nativeVideoControls":
2675                    this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed.
2676                    this.status.nativeVideoControls = this._uaBlocklist(this.options.nativeVideoControls);
2677                    this._restrictNativeVideoControls();
2678                    this._updateNativeVideoControls();
2679                    break;
2680                case "noFullWindow":
2681                    this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed.
2682                    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.
2683                    this.status.noFullWindow = this._uaBlocklist(this.options.noFullWindow);
2684                    this._restrictNativeVideoControls();
2685                    this._updateButtons();
2686                    break;
2687                case "noVolume":
2688                    this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed.
2689                    this.status.noVolume = this._uaBlocklist(this.options.noVolume);
2690                    this._updateVolume();
2691                    this._updateMute();
2692                    break;
2693                case "emulateHtml":
2694                    if (this.options[key] !== value) { // To avoid multiple event handlers being created, if true already.
2695                        this.options[key] = value;
2696                        if (value) {
2697                            this._emulateHtmlBridge();
2698                        } else {
2699                            this._destroyHtmlBridge();
2700                        }
2701                    }
2702                    break;
2703                case "timeFormat":
2704                    this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed.
2705                    break;
2706                case "keyEnabled":
2707                    this.options[key] = value;
2708                    if (!value && this === $.jPlayer.focus) {
2709                        $.jPlayer.focus = null;
2710                    }
2711                    break;
2712                case "keyBindings":
2713                    this.options[key] = $.extend(true, {}, this.options[key], value); // store a merged DEEP copy of it, incase not all properties changed.
2714                    break;
2715                case "audioFullScreen":
2716                    this.options[key] = value;
2717                    break;
2718                case "autoBlur":
2719                    this.options[key] = value;
2720                    break;
2721            }
2722
2723            return this;
2724        },
2725        // End of: (Options code adapted from ui.widget.js)
2726
2727        _refreshSize: function() {
2728            this._setSize(); // update status and jPlayer element size
2729            this._addUiClass(); // update the ui class
2730            this._updateSize(); // update internal sizes
2731            this._updateButtons();
2732            this._updateAutohide();
2733            this._trigger($.jPlayer.event.resize);
2734        },
2735        _setSize: function() {
2736            // Determine the current size from the options
2737            if (this.options.fullWindow) {
2738                this.status.width = this.options.sizeFull.width;
2739                this.status.height = this.options.sizeFull.height;
2740                this.status.cssClass = this.options.sizeFull.cssClass;
2741            } else {
2742                this.status.width = this.options.size.width;
2743                this.status.height = this.options.size.height;
2744                this.status.cssClass = this.options.size.cssClass;
2745            }
2746
2747            // Set the size of the jPlayer area.
2748            this.element.css({ 'width': this.status.width, 'height': this.status.height });
2749        },
2750        _addUiClass: function() {
2751            if (this.ancestorJq.length) {
2752                this.ancestorJq.addClass(this.status.cssClass);
2753            }
2754        },
2755        _removeUiClass: function() {
2756            if (this.ancestorJq.length) {
2757                this.ancestorJq.removeClass(this.status.cssClass);
2758            }
2759        },
2760        _updateSize: function() {
2761            // The poster uses show/hide so can simply resize it.
2762            this.internal.poster.jq.css({ 'width': this.status.width, 'height': this.status.height });
2763
2764            // Video html or flash resized if necessary at this time, or if native video controls being used.
2765            if (!this.status.waitForPlay && this.html.active && this.status.video || this.html.video.available && this.html.used && this.status.nativeVideoControls) {
2766                this.internal.video.jq.css({ 'width': this.status.width, 'height': this.status.height });
2767            } else if (!this.status.waitForPlay && this.flash.active && this.status.video) {
2768                this.internal.flash.jq.css({ 'width': this.status.width, 'height': this.status.height });
2769            }
2770        },
2771        _updateAutohide: function() {
2772            var self = this,
2773                event = "mousemove.jPlayer",
2774                namespace = ".jPlayerAutohide",
2775                eventType = event + namespace,
2776                handler = function(event) {
2777                    var moved = false,
2778                        deltaX, deltaY;
2779                    if (typeof self.internal.mouse !== "undefined") {
2780                        //get the change from last position to this position
2781                        deltaX = self.internal.mouse.x - event.pageX;
2782                        deltaY = self.internal.mouse.y - event.pageY;
2783                        moved = (Math.floor(deltaX) > 0) || (Math.floor(deltaY) > 0);
2784                    } else {
2785                        moved = true;
2786                    }
2787                    // store current position for next method execution
2788                    self.internal.mouse = {
2789                        x: event.pageX,
2790                        y: event.pageY
2791                    };
2792                    // if mouse has been actually moved, do the gui fadeIn/fadeOut
2793                    if (moved) {
2794                        self.css.jq.gui.fadeIn(self.options.autohide.fadeIn, function() {
2795                            clearTimeout(self.internal.autohideId);
2796                            self.internal.autohideId = setTimeout(function() {
2797                                self.css.jq.gui.fadeOut(self.options.autohide.fadeOut);
2798                            }, self.options.autohide.hold);
2799                        });
2800                    }
2801                };
2802
2803            if (this.css.jq.gui.length) {
2804
2805                // End animations first so that its callback is executed now.
2806                // Otherwise an in progress fadeIn animation still has the callback to fadeOut again.
2807                this.css.jq.gui.stop(true, true);
2808
2809                // Removes the fadeOut operation from the fadeIn callback.
2810                clearTimeout(this.internal.autohideId);
2811                // undefine mouse
2812                delete this.internal.mouse;
2813
2814                this.element.unbind(namespace);
2815                this.css.jq.gui.unbind(namespace);
2816
2817                if (!this.status.nativeVideoControls) {
2818                    if (this.options.fullWindow && this.options.autohide.full || !this.options.fullWindow && this.options.autohide.restored) {
2819                        this.element.bind(eventType, handler);
2820                        this.css.jq.gui.bind(eventType, handler);
2821                        this.css.jq.gui.hide();
2822                    } else {
2823                        this.css.jq.gui.show();
2824                    }
2825                } else {
2826                    this.css.jq.gui.hide();
2827                }
2828            }
2829        },
2830        fullScreen: function(event) {
2831            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.
2832            if (guiAction && this.options.useStateClassSkin && this.options.fullScreen) {
2833                this._setOption("fullScreen", false);
2834            } else {
2835                this._setOption("fullScreen", true);
2836            }
2837        },
2838        restoreScreen: function() {
2839            this._setOption("fullScreen", false);
2840        },
2841        _fullscreenAddEventListeners: function() {
2842            var self = this,
2843                fs = $.jPlayer.nativeFeatures.fullscreen;
2844
2845            if (fs.api.fullscreenEnabled) {
2846                if (fs.event.fullscreenchange) {
2847                    // Create the event handler function and store it for removal.
2848                    if (typeof this.internal.fullscreenchangeHandler !== 'function') {
2849                        this.internal.fullscreenchangeHandler = function() {
2850                            self._fullscreenchange();
2851                        };
2852                    }
2853                    document.addEventListener(fs.event.fullscreenchange, this.internal.fullscreenchangeHandler, false);
2854                }
2855                // No point creating handler for fullscreenerror.
2856                // Either logic avoids fullscreen occurring (w3c/moz), or their is no event on the browser (webkit).
2857            }
2858        },
2859        _fullscreenRemoveEventListeners: function() {
2860            var fs = $.jPlayer.nativeFeatures.fullscreen;
2861            if (this.internal.fullscreenchangeHandler) {
2862                document.removeEventListener(fs.event.fullscreenchange, this.internal.fullscreenchangeHandler, false);
2863            }
2864        },
2865        _fullscreenchange: function() {
2866            // If nothing is fullscreen, then we cannot be in fullscreen mode.
2867            if (this.options.fullScreen && !$.jPlayer.nativeFeatures.fullscreen.api.fullscreenElement()) {
2868                this._setOption("fullScreen", false);
2869            }
2870        },
2871        _requestFullscreen: function() {
2872            // Either the container or the jPlayer div
2873            var e = this.ancestorJq.length ? this.ancestorJq[0] : this.element[0],
2874                fs = $.jPlayer.nativeFeatures.fullscreen;
2875
2876            // This method needs the video element. For iOS and Android.
2877            if (fs.used.webkitVideo) {
2878                e = this.htmlElement.video;
2879            }
2880
2881            if (fs.api.fullscreenEnabled) {
2882                fs.api.requestFullscreen(e);
2883            }
2884        },
2885        _exitFullscreen: function() {
2886
2887            var fs = $.jPlayer.nativeFeatures.fullscreen,
2888              
2888  e;
2889
2890            // This method needs the video element. For iOS and Android.
2891            if (fs.used.webkitVideo) {
2892                e = this.htmlElement.video;
2893            }
2894
2895            if (fs.api.fullscreenEnabled) {
2896                fs.api.exitFullscreen(e);
2897            }
2898        },
2899        _html_initMedia: function(media) {
2900            // Remove any existing track elements
2901            var $media = $(this.htmlElement.media).empty();
2902
2903            // Create any track elements given with the media, as an Array of track Objects.
2904            $.each(media.track || [], function(i, v) {
2905                var track = document.createElement('track');
2906                track.setAttribute("kind", v.kind ? v.kind : "");
2907                track.setAttribute("src", v.src ? v.src : "");
2908                track.setAttribute("srclang", v.srclang ? v.srclang : "");
2909                track.setAttribute("label", v.label ? v.label : "");
2910                if (v.def) {
2911                    track.setAttribute("default", v.def);
2912                }
2913                $media.append(track);
2914            });
2915
2916            this.htmlElement.media.src = this.status.src;
2917
2918            if (this.options.preload !== 'none') {
2919                this._html_load(); // See function for comments
2920            }
2921            this._trigger($.jPlayer.event.timeupdate); // The flash generates this event for its solution.
2922        },
2923        _html_setFormat: function(media) {
2924            var self = this;
2925            // Always finds a format due to checks in setMedia()
2926            $.each(this.formats, function(priority, format) {
2927                if (self.html.support[format] && media[format]) {
2928                    self.status.src = media[format];
2929                    self.status.format[format] = true;
2930                    self.status.formatType = format;
2931                    return false;
2932                }
2933            });
2934        },
2935        _html_setAudio: function(media) {
2936            this._html_setFormat(media);
2937            this.htmlElement.media = this.htmlElement.audio;
2938            this._html_initMedia(media);
2939        },
2940        _html_setVideo: function(media) {
2941            this._html_setFormat(media);
2942            if (this.status.nativeVideoControls) {
2943                this.htmlElement.video.poster = this._validString(media.poster) ? media.poster : "";
2944            }
2945            this.htmlElement.media = this.htmlElement.video;
2946            this._html_initMedia(media);
2947        },
2948        _html_resetMedia: function() {
2949            if (this.htmlElement.media) {
2950                if (this.htmlElement.media.id === this.internal.video.id && !this.status.nativeVideoControls) {
2951                    this.internal.video.jq.css({ 'width': '0px', 'height': '0px' });
2952                }
2953                this.htmlElement.media.pause();
2954            }
2955        },
2956        _html_clearMedia: function() {
2957            if (this.htmlElement.media) {
2958                this.htmlElement.media.src = "about:blank";
2959                // The following load() is only required for Firefox 3.6 (PowerMacs).
2960                // Recent HTMl5 browsers only require the src change. Due to changes in W3C spec and load() effect.
2961                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.
2962            }
2963        },
2964        _html_load: function() {
2965            // This function remains to allow the early HTML5 browsers to work, such as Firefox 3.6
2966            // A change in the W3C spec for the media.load() command means that this is no longer necessary.
2967            // 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.
2968            if (this.status.waitForLoad) {
2969                this.status.waitForLoad = false;
2970                this.htmlElement.media.load();
2971            }
2972            clearTimeout(this.internal.htmlDlyCmdId);
2973        },
2974        _html_play: function(time) {
2975            var self = this,
2976                media = this.htmlElement.media;
2977
2978            this.androidFix.pause = false; // Cancel the pause fix.
2979
2980            this._html_load(); // Loads if required and clears any delayed commands.
2981
2982            // Setup the Android Fix.
2983            if (this.androidFix.setMedia) {
2984                this.androidFix.play = true;
2985                this.androidFix.time = time;
2986
2987            } else if (!isNaN(time)) {
2988
2989                // Attempt to play it, since iOS has been ignoring commands
2990                if (this.internal.cmdsIgnored) {
2991                    media.play();
2992                }
2993
2994                try {
2995                    // !media.seekable is for old HTML5 browsers, like Firefox 3.6.
2996                    // Checking seekable.length is important for iOS6 to work with setMedia().play(time)
2997                    if (!media.seekable || typeof media.seekable === "object" && media.seekable.length > 0) {
2998                        media.currentTime = time;
2999                        media.play();
3000                    } else {
3001                        throw 1;
3002                    }
3003                } catch (err) {
3004                    this.internal.htmlDlyCmdId = setTimeout(function() {
3005                        self.play(time);
3006                    }, 250);
3007                    return; // Cancel execution and wait for the delayed command.
3008                }
3009            } else {
3010                media.play();
3011            }
3012            this._html_checkWaitForPlay();
3013        },
3014        _html_pause: function(time) {
3015            var self = this,
3016                media = this.htmlElement.media;
3017
3018            this.androidFix.play = false; // Cancel the play fix.
3019
3020            if (time > 0) { // We do not want the stop() command, which does pause(0), causing a load operation.
3021                this._html_load(); // Loads if required and clears any delayed commands.
3022            } else {
3023                clearTimeout(this.internal.htmlDlyCmdId);
3024            }
3025
3026            // Order of these commands is important for Safari (Win) and IE9. Pause then change currentTime.
3027            media.pause();
3028
3029            // Setup the Android Fix.
3030            if (this.androidFix.setMedia) {
3031                this.androidFix.pause = true;
3032                this.androidFix.time = time;
3033
3034            } else if (!isNaN(time)) {
3035                try {
3036                    if (!media.seekable || typeof media.seekable === "object" && media.seekable.length > 0) {
3037                        media.currentTime = time;
3038                    } else {
3039                        throw 1;
3040                    }
3041                } catch (err) {
3042                    this.internal.htmlDlyCmdId = setTimeout(function() {
3043                        self.pause(time);
3044                    }, 250);
3045                    return; // Cancel execution and wait for the delayed command.
3046                }
3047            }
3048            if (time > 0) { // Avoids a setMedia() followed by stop() or pause(0) hiding the video play button.
3049                this._html_checkWaitForPlay();
3050            }
3051        },
3052        _html_playHead: function(percent) {
3053            var self = this,
3054                media = this.htmlElement.media;
3055
3056            this._html_load(); // Loads if required and clears any delayed commands.
3057
3058            // This playHead() method needs a refactor to apply the android fix.
3059
3060            try {
3061                if (typeof media.seekable === "object" && media.seekable.length > 0) {
3062                    media.currentTime = percent * media.seekable.end(media.seekable.length - 1) / 100;
3063                } else if (media.duration > 0 && !isNaN(media.duration)) {
3064                    media.currentTime = percent * media.duration / 100;
3065                } else {
3066                    throw "e";
3067                }
3068            } catch (err) {
3069                this.internal.htmlDlyCmdId = setTimeout(function() {
3070                    self.playHead(percent);
3071                }, 250);
3072                return; // Cancel execution and wait for the delayed command.
3073            }
3074            if (!this.status.waitForLoad) {
3075                this._html_checkWaitForPlay();
3076            }
3077        },
3078        _html_checkWaitForPlay: function() {
3079            if (this.status.waitForPlay) {
3080                this.status.waitForPlay = false;
3081                if (this.css.jq.videoPlay.length) {
3082                    this.css.jq.videoPlay.hide();
3083                }
3084                if (this.status.video) {
3085                    this.internal.poster.jq.hide();
3086                    this.internal.video.jq.css({ 'width': this.status.width, 'height': this.status.height });
3087                }
3088            }
3089        },
3090        _html_setProperty: function(property, value) {
3091            if (this.html.audio.available) {
3092                this.htmlElement.audio[property] = value;
3093            }
3094            if (this.html.video.available) {
3095                this.htmlElement.video[property] = value;
3096            }
3097        },
3098        _aurora_setAudio: function(media) {
3099            var self = this;
3100
3101            // Always finds a format due to checks in setMedia()
3102            $.each(this.formats, function(priority, format) {
3103                if (self.aurora.support[format] && media[format]) {
3104                    self.status.src = media[format];
3105                    self.status.format[format] = true;
3106                    self.status.formatType = format;
3107
3108                    return false;
3109                }
3110            });
3111
3112            this.aurora.player = new AV.Player.fromURL(this.status.src);
3113            this._addAuroraEventListeners(this.aurora.player, this.aurora);
3114
3115            if (this.options.preload === 'auto') {
3116                this._aurora_load();
3117                this.status.waitForLoad = false;
3118            }
3119        },
3120        _aurora_resetMedia: function() {
3121            if (this.aurora.player) {
3122                this.aurora.player.stop();
3123            }
3124        },
3125        _aurora_clearMedia: function() {
3126            // Nothing to clear.
3127        },
3128        _aurora_load: function() {
3129            if (this.status.waitForLoad) {
3130                this.status.waitForLoad = false;
3131                this.aurora.player.preload();
3132            }
3133        },
3134        _aurora_play: function(time) {
3135            if (!this.status.waitForLoad) {
3136                if (!isNaN(time)) {
3137                    this.aurora.player.seek(time);
3138                }
3139            }
3140            if (!this.aurora.player.playing) {
3141                this.aurora.player.play();
3142            }
3143            this.status.waitForLoad = false;
3144            this._aurora_checkWaitForPlay();
3145
3146            // No event from the player, update UI now.
3147            this._updateButtons(true);
3148            this._trigger($.jPlayer.event.play);
3149        },
3150        _aurora_pause: function(time) {
3151            if (!isNaN(time)) {
3152                this.aurora.player.seek(time * 1000);
3153            }
3154            this.aurora.player.pause();
3155
3156            if (time > 0) { // Avoids a setMedia() followed by stop() or pause(0) hiding the video play button.
3157                this._aurora_checkWaitForPlay();
3158            }
3159
3160            // No event from the player, update UI now.
3161            this._updateButtons(false);
3162            this._trigger($.jPlayer.event.pause);
3163        },
3164        _aurora_playHead: function(percent) {
3165            if (this.aurora.player.duration > 0) {
3166                // The seek() sould be in milliseconds, but the only codec that works with seek (aac.js) uses seconds.
3167                this.aurora.player.seek(percent * this.aurora.player.duration / 100); // Using seconds
3168            }
3169
3170            if (!this.status.waitForLoad) {
3171                this._aurora_checkWaitForPlay();
3172            }
3173        },
3174        _aurora_checkWaitForPlay: function() {
3175            if (this.status.waitForPlay) {
3176                this.status.waitForPlay = false;
3177            }
3178        },
3179        _aurora_volume: function(v) {
3180            this.aurora.player.volume = v * 100;
3181        },
3182        _aurora_mute: function(m) {
3183            if (m) {
3184                this.aurora.properties.lastvolume = this.aurora.player.volume;
3185                this.aurora.player.volume = 0;
3186            } else {
3187                this.aurora.player.volume = this.aurora.properties.lastvolume;
3188            }
3189            this.aurora.properties.muted = m;
3190        },
3191        _flash_setAudio: function(media) {
3192            var self = this;
3193            try {
3194                // Always finds a format due to checks in setMedia()
3195                $.each(this.formats, function(priority, format) {
3196                    if (self.flash.support[format] && media[format]) {
3197                        switch (format) {
3198                            case "m4a":
3199                            case "fla":
3200                                self._getMovie().fl_setAudio_m4a(media[format]);
3201                                break;
3202                            case "mp3":
3203                                self._getMovie().fl_setAudio_mp3(media[format]);
3204                                break;
3205                            case "rtmpa":
3206                                self._getMovie().fl_setAudio_rtmp(media[format]);
3207                                break;
3208                        }
3209                        self.status.src = media[format];
3210                        self.status.format[format] = true;
3211                        self.status.formatType = format;
3212                        return false;
3213                    }
3214                });
3215
3216                if (this.options.preload === 'auto') {
3217                    this._flash_load();
3218                    this.status.waitForLoad = false;
3219                }
3220            } catch (err) { this._flashError(err); }
3221        },
3222        _flash_setVideo: function(media) {
3223            var self = this;
3224            try {
3225                // Always finds a format due to checks in setMedia()
3226                $.each(this.formats, function(priority, format) {
3227                    if (self.flash.support[format] && media[format]) {
3228                        switch (format) {
3229                            case "m4v":
3230                            case "flv":
3231                                self._getMovie().fl_setVideo_m4v(media[format]);
3232                                break;
3233                            case "rtmpv":
3234                                self._getMovie().fl_setVideo_rtmp(media[format]);
3235                                break;
3236                        }
3237                        self.status.src = media[format];
3238                        self.status.format[format] = true;
3239                        self.status.formatType = format;
3240                        return false;
3241                    }
3242                });
3243
3244                if (this.options.preload === 'auto') {
3245                    this._flash_load();
3246                    this.status.waitForLoad = false;
3247                }
3248            } catch (err) { this._flashError(err); }
3249        },
3250        _flash_resetMedia: function() {
3251            this.internal.flash.jq.css({ 'width': '0px', 'height': '0px' }); // Must do via CSS as setting attr() to zero causes a jQuery error in IE.
3252            this._flash_pause(NaN);
3253        },
3254        _flash_clearMedia: function() {
3255            try {
3256                this._getMovie().fl_clearMedia();
3257            } catch (err) { this._flashError(err); }
3258        },
3259        _flash_load: function() {
3260            try {
3261                this._getMovie().fl_load();
3262            } catch (err) { this._flashError(err); }
3263            this.status.waitForLoad = false;
3264        },
3265        _flash_play: function(time) {
3266            try {
3267                this._getMovie().fl_play(time);
3268            } catch (err) { this._flashError(err); }
3269            this.status.waitForLoad = false;
3270            this._flash_checkWaitForPlay();
3271        },
3272        _flash_pause: function(time) {
3273            try {
3274                this._getMovie().fl_pause(time);
3275            } catch (err) { this._flashError(err); }
3276            if (time > 0) { // Avoids a setMedia() followed by stop() or pause(0) hiding the video play button.
3277                this.status.waitForLoad = false;
3278                this._flash_checkWaitForPlay();
3279            }
3280        },
3281        _flash_playHead: function(p) {
3282            try {
3283                this._getMovie().fl_play_head(p);
3284            } catch (err) { this._flashError(err); }
3285            if (!this.status.waitForLoad) {
3286                this._flash_checkWaitForPlay();
3287            }
3288        },
3289        _flash_checkWaitForPlay: function() {
3290            if (this.status.waitForPlay) {
3291                this.status.waitForPlay = false;
3292                if (this.css.jq.videoPlay.length) {
3293                    this.css.jq.videoPlay.hide();
3294                }
3295                if (this.status.video) {
3296                    this.internal.poster.jq.hide();
3297                    this.internal.flash.jq.css({ 'width': this.status.width, 'height': this.status.height });
3298                }
3299            }
3300        },
3301        _flash_volume: function(v) {
3302            try {
3303                this._getMovie().fl_volume(v);
3304            } catch (err) { this._flashError(err); }
3305        },
3306        _flash_mute: function(m) {
3307            try {
3308                this._getMovie().fl_mute(m);
3309            } catch (err) { this._flashError(err); }
3310        },
3311        _getMovie: function() {
3312            return document[this.internal.flash.id];
3313        },
3314        _getFlashPluginVersion: function() {
3315
3316            // _getFlashPluginVersion() code influenced by:
3317            // - FlashReplace 1.01: http://code.google.com/p/flashreplace/
3318            // - SWFObject 2.2: http://code.google.com/p/swfobject/
3319
3320            var version = 0,
3321                flash;
3322            if (window.ActiveXObject) {
3323                try {
3324                    flash = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
3325                    if (flash) { // flash will return null when ActiveX is disabled
3326                        var v = flash.GetVariable("$version");
3327                        if (v) {
3328                            v = v.split(" ")[1].split(",");
3329                            version = parseInt(v[0], 10) + "." + parseInt(v[1], 10);
3330                        }
3331                    }
3332                } catch (e) {}
3333            } else if (navigator.plugins && navigator.mimeTypes.length > 0) {
3334                flash = navigator.plugins["Shockwave Flash"];
3335                if (flash) {
3336                    version = navigator.plugins["Shockwave Flash"].description.replace(/.*\s(\d+\.\d+).*/, "$1");
3337                }
3338            }
3339            return version * 1; // Converts to a number
3340        },
3341        _checkForFlash: function(version) {
3342            var flashOk = false;
3343            if (this._getFlashPluginVersion() >= version) {
3344                flashOk = true;
3345            }
3346            return flashOk;
3347        },
3348        _validString: function(url) {
3349            return (url && typeof url === "string"); // Empty strings return false
3350        },
3351        _limitValue: function(value, min, max) {
3352            return (value < min) ? min : ((value > max) ? max : value);
3353        },
3354        _urlNotSetError: function(context) {
3355            this._error({
3356                type: $.jPlayer.error.URL_NOT_SET,
3357                context: context,
3358                message: $.jPlayer.errorMsg.URL_NOT_SET,
3359                hint: $.jPlayer.errorHint.URL_NOT_SET
3360            });
3361        },
3362        _flashError: function(error) {
3363            var errorType;
3364            if (!this.internal.ready) {
3365                errorType = "FLASH";
3366            } else {
3367                errorType = "FLASH_DISABLED";
3368            }
3369            this._error({
3370                type: $.jPlayer.error[errorType],
3371                context: this.internal.flash.swf,
3372                message: $.jPlayer.errorMsg[errorType] + error.message,
3373                hint: $.jPlayer.errorHint[errorType]
3374            });
3375            // Allow the audio player to recover if display:none and then shown again, or with position:fixed on Firefox.
3376            // This really only affects audio in a media player, as an audio player could easily move the jPlayer element away from such issues.
3377            this.internal.flash.jq.css({ 'width': '1px', 'height': '1px' });
3378        },
3379        _error: function(error) {
3380            this._trigger($.jPlayer.event.error, error);
3381            if (this.options.errorAlerts) {
3382                this._alert("Error!" + (error.message ? "\n" + error.message : "") + (error.hint ? "\n" + error.hint : "") + "\nContext: " + error.context);
3383            }
3384        },
3385        _warning: function(warning) {
3386            this._trigger($.jPlayer.event.warning, undefined, warning);
3387            if (this.options.warningAlerts) {
3388                this._alert("Warning!" + (warning.message ? "\n" + warning.message : "") + (warning.hint ? "\n" + warning.hint : "") + "\nContext: " + warning.context);
3389            }
3390        },
3391        _alert: function(message) {
3392            var msg = "jPlayer " + this.version.script + " : id='" + this.internal.self.id + "' : " + message;
3393            if (!this.options.consoleAlerts) {
3394                alert(msg);
3395            } else if (window.console && window.console.log) {
3396                window.console.log(msg);
3397            }
3398        },
3399        _emulateHtmlBridge: function() {
3400            var self = this;
3401
3402            // Emulate methods on jPlayer's DOM element.
3403            $.each($.jPlayer.emulateMethods.split(/\s+/g), function(i, name) {
3404                self.internal.domNode[name] = function(arg) {
3405                    self[name](arg);
3406                };
3407
3408            });
3409
3410            // Bubble jPlayer events to its DOM element.
3411            $.each($.jPlayer.event, function(eventName, eventType) {
3412                var nativeEvent = true;
3413                $.each($.jPlayer.reservedEvent.split(/\s+/g), function(i, name) {
3414                    if (name === eventName) {
3415                        nativeEvent = false;
3416                        return false;
3417                    }
3418                });
3419                if (nativeEvent) {
3420                    self.element.bind(eventType + ".jPlayer.jPlayerHtml", function() { // With .jPlayer & .jPlayerHtml namespaces.
3421                        self._emulateHtmlUpdate();
3422                        var domEvent = document.createEvent("Event");
3423                        domEvent.initEvent(eventName, false, true);
3424                        self.internal.domNode.dispatchEvent(domEvent);
3425                    });
3426                }
3427                // The error event would require a special case
3428            });
3429
3430            // 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.
3431        },
3432        _emulateHtmlUpdate: function() {
3433            var self = this;
3434
3435            $.each($.jPlayer.emulateStatus.split(/\s+/g), function(i, name) {
3436                self.internal.domNode[name] = self.status[name];
3437            });
3438            $.each($.jPlayer.emulateOptions.split(/\s+/g), function(i, name) {
3439                self.internal.domNode[name] = self.options[name];
3440            });
3441        },
3442        _destroyHtmlBridge: function() {
3443            var self = this;
3444
3445            // Bridge event handlers are also removed by destroy() through .jPlayer namespace.
3446            this.element.unbind(".jPlayerHtml"); // Remove all event handlers created by the jPlayer bridge. So you can change the emulateHtml option.
3447
3448            // Remove the methods and properties
3449            var emulated = $.jPlayer.emulateMethods + " " + $.jPlayer.emulateStatus + " " + $.jPlayer.emulateOptions;
3450            $.each(emulated.split(/\s+/g), function(i, name) {
3451                delete self.internal.domNode[name];
3452            });
3453        }
3454    };
3455
3456    $.jPlayer.error = {
3457        FLASH: "e_flash",
3458        FLASH_DISABLED: "e_flash_disabled",
3459        NO_SOLUTION: "e_no_solution",
3460        NO_SUPPORT: "e_no_support",
3461        URL: "e_url",
3462        URL_NOT_SET: "e_url_not_set",
3463        VERSION: "e_version"
3464    };
3465
3466    $.jPlayer.errorMsg = {
3467        FLASH: "jPlayer's Flash fallback is not configured correctly, or a command was issued before the jPlayer Ready event. Details: ", // Used in: _flashError()
3468        FLASH_DISABLED: "jPlayer's Flash fallback has been disabled by the browser due to the CSS rules you have used. Details: ", // Used in: _flashError()
3469        NO_SOLUTION: "No solution can be found by jPlayer in this browser. Neither HTML nor Flash can be used.", // Used in: _init()
3470        NO_SUPPORT: "It is not possible to play any media format provided in setMedia() on this browser using your current options.", // Used in: setMedia()
3471        URL: "Media URL could not be loaded.", // Used in: jPlayerFlashEvent() and _addHtmlEventListeners()
3472        URL_NOT_SET: "Attempt to issue media playback commands, while no media url is set.", // Used in: load(), play(), pause(), stop() and playHead()
3473        VERSION: "jPlayer " + $.jPlayer.prototype.version.script + " needs Jplayer.swf version " + $.jPlayer.prototype.version.needFlash + " but found " // Used in: jPlayerReady()
3474    };
3475
3476    $.jPlayer.errorHint = {
3477        FLASH: "Check your swfPath option and that Jplayer.swf is there.",
3478        FLASH_DISABLED: "Check that you have not display:none; the jPlayer entity or any ancestor.",
3479        NO_SOLUTION: "Review the jPlayer options: support and supplied.",
3480        NO_SUPPORT: "Video or audio formats defined in the supplied option are missing.",
3481        URL: "Check media URL is valid.",
3482        URL_NOT_SET: "Use setMedia() to set the media URL.",
3483        VERSION: "Update jPlayer files."
3484    };
3485
3486    $.jPlayer.warning = {
3487        CSS_SELECTOR_COUNT: "e_css_selector_count",
3488        CSS_SELECTOR_METHOD: "e_css_selector_method",
3489        CSS_SELECTOR_STRING: "e_css_selector_string",
3490        OPTION_KEY: "e_option_key"
3491    };
3492
3493    $.jPlayer.warningMsg = {
3494        CSS_SELECTOR_COUNT: "The number of css selectors found did not equal one: ",
3495        CSS_SELECTOR_METHOD: "The methodName given in jPlayer('cssSelector') is not a valid jPlayer method.",
3496        CSS_SELECTOR_STRING: "The methodCssSelector given in jPlayer('cssSelector') is not a String or is empty.",
3497        OPTION_KEY: "The option requested in jPlayer('option') is undefined."
3498    };
3499
3500    $.jPlayer.warningHint = {
3501        CSS_SELECTOR_COUNT: "Check your css selector and the ancestor.",
3502        CSS_SELECTOR_METHOD: "Check your method name.",
3503        CSS_SELECTOR_STRING: "Check your css selector is a string.",
3504        OPTION_KEY: "Check your option name."
3505    };
3506}));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.