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