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