2  * jPlayer Plugin for jQuery JavaScript Library
 
   3  * http://www.jplayer.org
 
   5  * Copyright (c) 2009 - 2014 Happyworm Ltd
 
   6  * Licensed under the MIT license.
 
   7  * http://opensource.org/licenses/MIT
 
   9  * Author: Mark J Panaghiston
 
  11  * Date: 14th December 2014
 
  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"
 
  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') {
 
  26                 factory(require('jquery')); // jQuery Switch
 
  27                 //factory(require('zepto')); // Zepto Switch
 
  30                 if(root.jQuery) { // Use jQuery if available
 
  32                 } else { // Otherwise, use Zepto
 
  36 }(this, function ($, undefined) {
 
  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 ) {
 
  41                 var isMethodCall = typeof options === "string",
 
  42                         args = Array.prototype.slice.call( arguments, 1 ),
 
  45                 // allow multiple hashes to be passed on init
 
  46                 options = !isMethodCall && args.length ?
 
  47                         $.extend.apply( null, [ true, options ].concat(args) ) :
 
  50                 // prevent calls to internal methods
 
  51                 if ( isMethodCall && options.charAt( 0 ) === "_" ) {
 
  56                         this.each(function() {
 
  57                                 var instance = $(this).data( name ),
 
  58                                         methodValue = instance && $.isFunction( instance[options] ) ?
 
  59                                                 instance[ options ].apply( instance, args ) :
 
  61                                 if ( methodValue !== instance && methodValue !== undefined ) {
 
  62                                         returnValue = methodValue;
 
  67                         this.each(function() {
 
  68                                 var instance = $(this).data( name );
 
  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.
 
  73                                         $(this).data( name, new $.jPlayer( options, this ) );
 
  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, {},
 
  90                         this.element.bind( "remove.jPlayer", function() {
 
  96         // End of: (Adapted from jquery.ui.widget.js (1.8.7))
 
  98         // Zepto is missing one of the animation methods.
 
  99         if(typeof $.fn.stop !== 'function') {
 
 100                 $.fn.stop = function() {};
 
 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";
 
 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";
 
 111         // Events generated by jPlayer
 
 112         $.jPlayer.event = {};
 
 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
 
 124                         // Other events match HTML5 spec.
 
 148                         $.jPlayer.event[ this ] = 'jPlayer_' + this;
 
 152         $.jPlayer.htmlEvent = [ // These HTML events are bubbled through to the jPlayer event, without any internal action.
 
 154                 // "progress", // jPlayer uses internally before bubbling.
 
 155                 // "suspend", // jPlayer uses internally before bubbling.
 
 157                 // "error", // jPlayer uses internally before bubbling.
 
 160                 // "play", // jPlayer uses internally before bubbling.
 
 161                 // "pause", // jPlayer uses internally before bubbling.
 
 163                 // "loadeddata", // jPlayer uses internally before bubbling.
 
 164                 // "waiting", // jPlayer uses internally before bubbling.
 
 165                 // "playing", // jPlayer uses internally before bubbling.
 
 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.
 
 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");
 
 186         // Default for jPlayer option.timeFormat
 
 187         $.jPlayer.timeFormat = {
 
 198         var ConvertTime = function() {
 
 201         ConvertTime.prototype = {
 
 204                                 timeFormat: $.jPlayer.timeFormat
 
 207                 time: function(s) { // function used on jPlayer.prototype._convertTime to enable per instance options.
 
 208                         s = (s && typeof s === 'number') ? s : 0;
 
 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,
 
 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 : "";
 
 226         var myConvertTime = new ConvertTime();
 
 227         $.jPlayer.convertTime = function(s) {
 
 228                 return myConvertTime.time(s);
 
 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();
 
 236                 var rwebkit = /(webkit)[ \/]([\w.]+)/;
 
 237                 var ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/;
 
 238                 var rmsie = /(msie) ([\w.]+)/;
 
 239                 var rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/;
 
 241                 var match = rwebkit.exec( ua ) ||
 
 244                         ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
 
 247                 return { browser: match[1] || "", version: match[2] || "0" };
 
 250         // Platform sniffer for detecting mobile devices
 
 251         $.jPlayer.uaPlatform = function( userAgent ) {
 
 252                 var ua = userAgent.toLowerCase();
 
 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)/;
 
 260                 var platform = rplatform.exec( ua ) || [];
 
 261                 var tablet = rtablet.exec( ua ) ||
 
 262                         !rmobile.exec( ua ) && randroid.exec( ua ) ||
 
 266                         platform[1] = platform[1].replace(/\s/g, "_"); // Change whitespace to underscore. Enables dot notation.
 
 269                 return { platform: platform[1] || "", tablet: tablet[1] || "" };
 
 272         $.jPlayer.browser = {
 
 274         $.jPlayer.platform = {
 
 277         var browserMatch = $.jPlayer.uaBrowser(navigator.userAgent);
 
 278         if ( browserMatch.browser ) {
 
 279                 $.jPlayer.browser[ browserMatch.browser ] = true;
 
 280                 $.jPlayer.browser.version = browserMatch.version;
 
 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;
 
 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() {
 
 293                 if ($.jPlayer.browser.msie) {
 
 294                         if (document.documentMode) { // IE8 or later
 
 295                                 docMode = document.documentMode;
 
 297                                 docMode = 5; // Assume quirks mode unless proven otherwise
 
 298                                 if (document.compatMode) {
 
 299                                         if (document.compatMode === "CSS1Compat") {
 
 300                                                 docMode = 7; // standards mode
 
 307         $.jPlayer.browser.documentMode = $.jPlayer.getDocMode();
 
 309         $.jPlayer.nativeFeatures = {
 
 312                         /* Fullscreen function naming influenced by W3C naming.
 
 313                          * No support for: Mozilla Proposal: https://wiki.mozilla.org/Gecko:FullScreenAPI
 
 317                                 v = d.createElement('video'),
 
 319                                         // http://www.w3.org/TR/fullscreen/
 
 328                                         // https://developer.mozilla.org/en-US/docs/DOM/Using_fullscreen_mode
 
 330                                                 'mozFullScreenEnabled',
 
 331                                                 'mozFullScreenElement',
 
 332                                                 'mozRequestFullScreen',
 
 333                                                 'mozCancelFullScreen',
 
 334                                                 'mozfullscreenchange',
 
 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
 
 341                                                 'webkitCurrentFullScreenElement',
 
 342                                                 'webkitRequestFullScreen',
 
 343                                                 'webkitCancelFullScreen',
 
 344                                                 'webkitfullscreenchange',
 
 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'
 
 351                                                 'webkitSupportsFullscreen',
 
 352                                                 'webkitDisplayingFullscreen',
 
 353                                                 'webkitEnterFullscreen',
 
 354                                                 'webkitExitFullscreen',
 
 360                                                 'msFullscreenElement',
 
 361                                                 'msRequestFullscreen',
 
 363                                                 'MSFullscreenChange',
 
 376                         this.fullscreen = fs = {
 
 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'
 
 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];
 
 398                                 var s = spec[fs.spec];
 
 400                                         fullscreenEnabled: true,
 
 401                                         fullscreenElement: function(elem) {
 
 402                                                 elem = elem ? elem : d; // Video element required for webkitVideo
 
 405                                         requestFullscreen: function(elem) {
 
 406                                                 return elem[s[2]](); // Chrome and Opera want parameter (Element.ALLOW_KEYBOARD_INPUT) but Safari fails if flag used.
 
 408                                         exitFullscreen: function(elem) {
 
 409                                                 elem = elem ? elem : d; // Video element required for webkitVideo
 
 414                                         fullscreenchange: s[4],
 
 415                                         fullscreenerror: s[5]
 
 419                                         fullscreenEnabled: false,
 
 420                                         fullscreenElement: function() {
 
 423                                         requestFullscreen: function() {},
 
 424                                         exitFullscreen: function() {}
 
 430         $.jPlayer.nativeFeatures.init();
 
 432         // The keyboard control system.
 
 434         // The current jPlayer instance in focus.
 
 435         $.jPlayer.focus = null;
 
 437         // The list of element node names to ignore with key controls.
 
 438         $.jPlayer.keyIgnoreElementNames = "A INPUT TEXTAREA SELECT BUTTON";
 
 440         // The function that deals with key presses.
 
 441         var keyBindings = function(event) {
 
 442                 var f = $.jPlayer.focus,
 
 445                 // A jPlayer instance must be in focus. ie., keyEnabled and the last one played.
 
 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()) {
 
 452                                         return false; // exit each.
 
 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()
 
 460                                                 (binding && $.isFunction(binding.fn)) &&
 
 461                                                 ((typeof binding.key === 'number' && event.which === binding.key) ||
 
 462                                                 (typeof binding.key === 'string' && event.key === binding.key))
 
 464                                                 event.preventDefault(); // Key being used by jPlayer, so prevent default operation.
 
 466                                                 return false; // exit each.
 
 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);
 
 478                         $(document.documentElement).bind(event, keyBindings);
 
 482         // Enable the global key control handler ready for any jPlayer instance with the keyEnabled option enabled.
 
 483         $.jPlayer.keys(true);
 
 485         $.jPlayer.prototype = {
 
 486                 count: 0, // Static Variable: Change it via prototype.
 
 487                 version: { // Static Object
 
 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.
 
 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.
 
 504                         defaultPlaybackRate: 1,
 
 505                         minPlaybackRate: 0.5,
 
 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", // *
 
 515                                 seekBar: ".jp-seek-bar",
 
 516                                 playBar: ".jp-play-bar",
 
 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",
 
 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.
 
 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"
 
 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
 
 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.
 
 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");
 
 561                                         $(this).unbind(".jPlayerRepeat");
 
 564                         nativeVideoControls: {
 
 565                                 // Works well on standard browsers.
 
 566                                 // Phone and tablet browsers can have problems with the controls disappearing.
 
 569                                 msie: /msie [0-6]\./,
 
 570                                 ipad: /ipad.*?os [0-4]\./,
 
 573                                 android_pad: /android [0-3]\.(?!.*?mobile)/,
 
 574                                 android_phone: /(?=.*android)(?!.*chrome)(?=.*mobile)/,
 
 575                                 blackberry: /blackberry/,
 
 576                                 windows_ce: /windows ce/,
 
 577                                 iemobile: /iemobile/,
 
 584                                 android_pad: /android(?!.*?mobile)/,
 
 585                                 android_phone: /android.*?mobile/,
 
 586                                 blackberry: /blackberry/,
 
 587                                 windows_ce: /windows ce/,
 
 588                                 iemobile: /iemobile/,
 
 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
 
 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.
 
 604                                                 if(f.status.paused) {
 
 614                                                 if(f.status.video || f.options.audioFullScreen) {
 
 615                                                         f._setOption("fullScreen", !f.options.fullScreen);
 
 622                                                 f._muted(!f.options.muted);
 
 628                                                 f.volume(f.options.volume + 0.1);
 
 634                                                 f.volume(f.options.volume - 0.1);
 
 640                                                 f._loop(!f.options.loop);
 
 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().
 
 670                                 cssClass: "jp-video-270p"
 
 675                                 cssClass: "jp-video-full"
 
 678                 instances: {}, // Static Object
 
 679                 status: { // Instanced in _init()
 
 685                         waitForPlay: true, // Same as waitForLoad except in case where preloading.
 
 688                         video: false, // True if playing a video
 
 690                         currentPercentRelative: 0,
 
 691                         currentPercentAbsolute: 0,
 
 695                         videoWidth: 0, // Intrinsic width of the video in pixels.
 
 696                         videoHeight: 0, // Intrinsic height of the video in pixels.
 
 699                         playbackRate: 1, // Warning - Now both an option and a status property
 
 702 /*              Persistant status properties created dynamically at _init():
 
 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.
 
 713                 internal: { // Instanced in _init()
 
 715                         // instance: undefined
 
 716                         // domNode: undefined
 
 717                         // htmlDlyCmdId: undefined
 
 718                         // autohideId: undefined
 
 722                 solution: { // Static Object: Defines the solutions built in jPlayer.
 
 727                 // 'MPEG-4 support' : canPlayType('video/mp4; codecs="mp4v.20.8"')
 
 728                 format: { // Static Object
 
 735                                 codec: 'audio/mp4; codecs="mp4a.40.2"',
 
 739                         m3u8a: { // AAC / MP4 / Apple HLS
 
 740                                 codec: 'application/vnd.apple.mpegurl; codecs="mp4a.40.2"',
 
 745                                 codec: 'audio/mpegurl',
 
 750                                 codec: 'audio/ogg; codecs="vorbis, opus"',
 
 755                                 codec: 'audio/x-flac',
 
 760                                 codec: 'audio/wav; codecs="1"',
 
 765                                 codec: 'audio/webm; codecs="vorbis"',
 
 770                                 codec: 'audio/x-flv',
 
 774                         rtmpa: { // RTMP AUDIO
 
 775                                 codec: 'audio/rtmp; codecs="rtmp"',
 
 779                         m4v: { // H.264 / MP4
 
 780                                 codec: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"',
 
 784                         m3u8v: { // H.264 / AAC / MP4 / Apple HLS
 
 785                                 codec: 'application/vnd.apple.mpegurl; codecs="avc1.42E01E, mp4a.40.2"',
 
 790                                 codec: 'audio/mpegurl',
 
 795                                 codec: 'video/ogg; codecs="theora, vorbis"',
 
 800                                 codec: 'video/webm; codecs="vorbis, vp8"',
 
 805                                 codec: 'video/x-flv',
 
 809                         rtmpv: { // RTMP VIDEO
 
 810                                 codec: 'video/rtmp; codecs="rtmp"',
 
 818                         this.element.empty();
 
 820                         this.status = $.extend({}, this.status); // Copy static to unique instance.
 
 821                         this.internal = $.extend({}, this.internal); // Copy static to unique instance.
 
 823                         // Initialize the time format
 
 824                         this.options.timeFormat = $.extend({}, $.jPlayer.timeFormat, this.options.timeFormat);
 
 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;
 
 829                         this.internal.domNode = this.element.get(0);
 
 831                         // Add key bindings focus to 1st jPlayer instanced with key control enabled.
 
 832                         if(this.options.keyEnabled && !$.jPlayer.focus) {
 
 833                                 $.jPlayer.focus = this;
 
 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.
 
 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
 
 843                         if($.jPlayer.platform.android) {
 
 844                                 this.options.preload = this.options.preload !== 'auto' ? 'metadata' : 'auto'; // Default to metadata, but allow auto.
 
 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.
 
 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.
 
 861                         this.css.cs = {}; // Holds the css selector strings
 
 862                         this.css.jq = {}; // Holds jQuery selectors. ie., $(css.cs.method)
 
 864                         this.ancestorJq = []; // Holds jQuery selector of cssSelectorAncestor. Init would use $() instead of [], but it is only 1.4+
 
 866                         this.options.volume = this._limitValue(this.options.volume, 0, 1); // Limit volume value's bounds.
 
 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) {
 
 880                                                 self.formats.push(format);
 
 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) {
 
 897                                                 self.solutions.push(solution);
 
 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) {
 
 914                                                 self.aurora.formats.push(format);
 
 919                         this.internal.instance = "jp_" + this.count;
 
 920                         this.instances[this.internal.instance] = this.element;
 
 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);
 
 927                         this.internal.self = $.extend({}, {
 
 928                                 id: this.element.attr("id"),
 
 931                         this.internal.audio = $.extend({}, {
 
 932                                 id: this.options.idPrefix + "_audio_" + this.count,
 
 935                         this.internal.video = $.extend({}, {
 
 936                                 id: this.options.idPrefix + "_video_" + this.count,
 
 939                         this.internal.flash = $.extend({}, {
 
 940                                 id: this.options.idPrefix + "_flash_" + this.count,
 
 942                                 swf: this.options.swfPath + (this.options.swfPath.toLowerCase().slice(-4) !== ".swf" ? (this.options.swfPath && this.options.swfPath.slice(-1) !== "/" ? "/" : "") + "jquery.jplayer.swf" : "")
 
 944                         this.internal.poster = $.extend({}, {
 
 945                                 id: this.options.idPrefix + "_poster_" + this.count,
 
 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.
 
 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;
 
 964                         // Now required types are known, finish the options default settings.
 
 965                         if(this.require.video) {
 
 966                                 this.options = $.extend(true, {},
 
 971                                 this.options = $.extend(true, {},
 
 976                         this._setSize(); // update status and jPlayer element size
 
 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);
 
 983                         // Create event handlers if native fullscreen is supported
 
 984                         if($.jPlayer.nativeFeatures.fullscreen.api.fullscreenEnabled) {
 
 985                                 this._fullscreenAddEventListeners();
 
 988                         // The native controls are only for video and are disabled when audio is also used.
 
 989                         this._restrictNativeVideoControls();
 
 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();
 
 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);
 
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.
 
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.
 
1021                         this.flash.available = this._checkForFlash(10.1);
 
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;
 
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;
 
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;
 
1045                                                                 audioCanPlay = true;
 
1049                                         self[solution].desired = (self.require.audio && !audioCanPlay) || (self.require.video && !videoCanPlay);
 
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;
 
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;
 
1074                         // Init solution active state and the event gates to false.
 
1075                         this._resetActive();
 
1078                         // Set up the css selectors for the control and feedback entities.
 
1079                         this._cssSelectorAncestor(this.options.cssSelectorAncestor);
 
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)) {
 
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
 
1089                                 if(this.css.jq.noSolution.length) {
 
1090                                         this.css.jq.noSolution.show();
 
1093                                 if(this.css.jq.noSolution.length) {
 
1094                                         this.css.jq.noSolution.hide();
 
1098                         // Add the flash solution if it is being used.
 
1099                         if(this.flash.used) {
 
1101                                 flashVars = 'jQuery=' + encodeURI(this.options.noConflict) + '&id=' + encodeURI(this.internal.self.id) + '&vol=' + this.options.volume + '&muted=' + this.options.muted;
 
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. 
 
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>';
 
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 + '" />'
 
1117                                         htmlObj = document.createElement(objStr);
 
1118                                         for(var i=0; i < paramStr.length; i++) {
 
1119                                                 htmlObj.appendChild(document.createElement(paramStr[i]));
 
1122                                         var createParam = function(el, n, v) {
 
1123                                                 var p = document.createElement("param");
 
1124                                                 p.setAttribute("name", n);      
 
1125                                                 p.setAttribute("value", v);
 
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);
 
1143                                 this.element.append(htmlObj);
 
1144                                 this.internal.flash.jq = $(htmlObj);
 
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');
 
1152                                 this.status.playbackRateEnabled = false;
 
1155                         this._updatePlaybackRate();
 
1157                         // Add the HTML solution if being used.
 
1158                         if(this.html.used) {
 
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);
 
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});
 
1175                                                 this.internal.video.jq.css({'width':'0px', 'height':'0px'}); // Using size 0x0 since a .hide() causes issues in iOS
 
1177                                         this.internal.video.jq.bind("click.jPlayer", function() {
 
1178                                                 self._trigger($.jPlayer.event.click);
 
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.
 
1188                         // Create the bridge that emulates the HTML Media element on the jPlayer DIV
 
1189                         if( this.options.emulateHtml ) {
 
1190                                 this._emulateHtmlBridge();
 
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);
 
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();
 
1209                         $.jPlayer.prototype.count++; // Change static variable via prototype.
 
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.
 
1215                         // Clear the media to reset the GUI and stop any downloads. Streams on some browsers had persited. (Chrome)
 
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("");
 
1223                         if(this.css.jq.duration.length) {
 
1224                                 this.css.jq.duration.text("");
 
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.
 
1230                                         jq.unbind(".jPlayer");
 
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");
 
1238                         // Remove the fullscreen event handlers
 
1239                         this._fullscreenRemoveEventListeners();
 
1240                         // Remove key bindings
 
1241                         if(this === $.jPlayer.focus) {
 
1242                                 $.jPlayer.focus = null;
 
1244                         // Destroy the HTML bridge.
 
1245                         if(this.options.emulateHtml) {
 
1246                                 this._destroyHtmlBridge();
 
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
 
1252                         delete this.instances[this.internal.instance]; // Clear the instance on the static instance object
 
1254                 destroyRemoved: function() { // Destroy any instances that have gone away.
 
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];
 
1265                 enable: function() { // Plan to implement
 
1266                         // options.disabled = false
 
1268                 disable: function () { // Plan to implement
 
1269                         // options.disabled = true
 
1271                 _testCanPlayType: function(elem) {
 
1272                         // IE9 on Win Server 2008 did not implement canPlayType(), but it has the property.
 
1274                                 elem.canPlayType(this.format.mp3.codec); // The type is irrelevant.
 
1280                 _testPlaybackRate: function(type) {
 
1281                         // type: String 'audio' or 'video'
 
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.
 
1287                                 if('playbackRate' in el) {
 
1288                                         el.playbackRate = rate;
 
1289                                         return el.playbackRate === rate;
 
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(),
 
1303                         $.each(list, function(p, re) {
 
1304                                 if(re && re.test(ua)) {
 
1306                                         return false; // exit $.each.
 
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;
 
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'});
 
1336                 _addHtmlEventListeners: function(mediaElement, entity) {
 
1338                         mediaElement.preload = this.options.preload;
 
1339                         mediaElement.muted = this.options.muted;
 
1340                         mediaElement.volume = this.options.volume;
 
1342                         if(this.status.playbackRateEnabled) {
 
1343                                 mediaElement.defaultPlaybackRate = this.options.defaultPlaybackRate;
 
1344                                 mediaElement.playbackRate = this.options.playbackRate;
 
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
 
1351                         mediaElement.addEventListener("progress", function() {
 
1353                                         if(self.internal.cmdsIgnored && this.readyState > 0) { // Detect iOS executed the command
 
1354                                                 self.internal.cmdsIgnored = false;
 
1356                                         self._getHtmlStatus(mediaElement);
 
1357                                         self._updateInterface();
 
1358                                         self._trigger($.jPlayer.event.progress);
 
1361                         mediaElement.addEventListener("loadeddata", function() {
 
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);
 
1368                                         if(self.androidFix.pause) { // Pause Android audio at time - performing the fix.
 
1369                                                 self.androidFix.pause = false;
 
1370                                                 self.pause(self.androidFix.time);
 
1372                                         self._trigger($.jPlayer.event.loadeddata);
 
1375                         mediaElement.addEventListener("timeupdate", function() {
 
1377                                         self._getHtmlStatus(mediaElement);
 
1378                                         self._updateInterface();
 
1379                                         self._trigger($.jPlayer.event.timeupdate);
 
1382                         mediaElement.addEventListener("durationchange", function() {
 
1384                                         self._getHtmlStatus(mediaElement);
 
1385                                         self._updateInterface();
 
1386                                         self._trigger($.jPlayer.event.durationchange);
 
1389                         mediaElement.addEventListener("play", function() {
 
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);
 
1396                         mediaElement.addEventListener("playing", function() {
 
1398                                         self._updateButtons(true);
 
1400                                         self._trigger($.jPlayer.event.playing);
 
1403                         mediaElement.addEventListener("pause", function() {
 
1405                                         self._updateButtons(false);
 
1406                                         self._trigger($.jPlayer.event.pause);
 
1409                         mediaElement.addEventListener("waiting", function() {
 
1412                                         self._trigger($.jPlayer.event.waiting);
 
1415                         mediaElement.addEventListener("seeking", function() {
 
1418                                         self._trigger($.jPlayer.event.seeking);
 
1421                         mediaElement.addEventListener("seeked", function() {
 
1424                                         self._trigger($.jPlayer.event.seeked);
 
1427                         mediaElement.addEventListener("volumechange", function() {
 
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;
 
1434                                         self._updateVolume();
 
1435                                         self._trigger($.jPlayer.event.volumechange);
 
1438                         mediaElement.addEventListener("ratechange", function() {
 
1440                                         self.options.defaultPlaybackRate = mediaElement.defaultPlaybackRate;
 
1441                                         self.options.playbackRate = mediaElement.playbackRate;
 
1442                                         self._updatePlaybackRate();
 
1443                                         self._trigger($.jPlayer.event.ratechange);
 
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.
 
1449                                         self._trigger($.jPlayer.event.suspend);
 
1452                         mediaElement.addEventListener("ended", function() {
 
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.)
 
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);
 
1466                         mediaElement.addEventListener("error", function() {
 
1468                                         self._updateButtons(false);
 
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'});
 
1477                                                 if(self._validString(self.status.media.poster) && !self.status.nativeVideoControls) {
 
1478                                                         self.internal.poster.jq.show();
 
1480                                                 if(self.css.jq.videoPlay.length) {
 
1481                                                         self.css.jq.videoPlay.show();
 
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
 
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() {
 
1496                                                 self._trigger($.jPlayer.event[eventType]);
 
1501                 _addAuroraEventListeners : function(player, entity) {
 
1503                         //player.preload = this.options.preload;
 
1504                         //player.muted = this.options.muted;
 
1505                         player.volume = this.options.volume * 100;
 
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
 
1511                         player.on("progress", function() {
 
1513                                         if(self.internal.cmdsIgnored && this.readyState > 0) { // Detect iOS executed the command
 
1514                                                 self.internal.cmdsIgnored = false;
 
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);
 
1525                         player.on("ready", function() {
 
1527                                         self._trigger($.jPlayer.event.loadeddata);
 
1530                         player.on("duration", function() {
 
1532                                         self._getAuroraStatus(player);
 
1533                                         self._updateInterface();
 
1534                                         self._trigger($.jPlayer.event.durationchange);
 
1537                         player.on("end", function() {
 
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);
 
1546                         player.on("error", function() {
 
1548                                         self._updateButtons(false);
 
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'});
 
1556                                                 if(self._validString(self.status.media.poster) && !self.status.nativeVideoControls) {
 
1557                                                         self.internal.poster.jq.show();
 
1559                                                 if(self.css.jq.videoPlay.length) {
 
1560                                                         self.css.jq.videoPlay.show();
 
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
 
1572                 _getHtmlStatus: function(media, override) {
 
1573                         var ct = 0, cpa = 0, sp = 0, cpr = 0;
 
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;
 
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.
 
1597                         this.status.seekPercent = sp;
 
1598                         this.status.currentPercentRelative = cpr;
 
1599                         this.status.currentPercentAbsolute = cpa;
 
1600                         this.status.currentTime = ct;
 
1602                         this.status.remaining = this.status.duration - this.status.currentTime;
 
1604                         this.status.videoWidth = media.videoWidth;
 
1605                         this.status.videoHeight = media.videoHeight;
 
1607                         this.status.readyState = media.readyState;
 
1608                         this.status.networkState = media.networkState;
 
1609                         this.status.playbackRate = media.playbackRate;
 
1610                         this.status.ended = media.ended;
 
1612                 _getAuroraStatus: function(player, override) {
 
1613                         var ct = 0, cpa = 0, sp = 0, cpr = 0;
 
1615                         this.status.duration = player.duration / 1000;
 
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;
 
1633                         this.status.seekPercent = sp;
 
1634                         this.status.currentPercentRelative = cpr;
 
1635                         this.status.currentPercentAbsolute = cpa;
 
1636                         this.status.currentTime = ct;
 
1638                         this.status.remaining = this.status.duration - this.status.currentTime;
 
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;
 
1645                 _resetStatus: function() {
 
1646                         this.status = $.extend({}, this.status, $.jPlayer.prototype.status); // Maintains the status properties that persist through a reset.
 
1648                 _trigger: function(eventType, error, warning) { // eventType always valid as called using $.jPlayer.event.eventType
 
1649                         var event = $.Event(eventType);
 
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
 
1658                                 event.jPlayer.error = $.extend({}, error);
 
1661                                 event.jPlayer.warning = $.extend({}, warning);
 
1663                         this.element.trigger(event);
 
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.
 
1671                                         this.version.flash = status.version;
 
1672                                         if(this.version.needFlash !== this.version.flash) {
 
1674                                                         type: $.jPlayer.error.VERSION,
 
1675                                                         context: this.version.flash,
 
1676                                                         message: $.jPlayer.errorMsg.VERSION + this.version.flash,
 
1677                                                         hint: $.jPlayer.errorHint.VERSION
 
1680                                         this._trigger($.jPlayer.event.repeat); // Trigger the repeat event so its handler can initialize itself with the loop option.
 
1681                                         this._trigger(eventType);
 
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.
 
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) {
 
1689                                                 // Send the current status to the Flash now that it is ready (available) again.
 
1690                                                 if(this.status.srcSet) {
 
1692                                                         // Need to read original status before issuing the setMedia command.
 
1693                                                         var     currentTime = this.status.currentTime,
 
1694                                                                 paused = this.status.paused; 
 
1696                                                         this.setMedia(this.status.media);
 
1697                                                         this.volumeWorker(this.options.volume);
 
1698                                                         if(currentTime > 0) {
 
1700                                                                         this.pause(currentTime);
 
1702                                                                         this.play(currentTime);
 
1706                                                 this._trigger($.jPlayer.event.flashreset);
 
1710                         if(this.flash.gate) {
 
1712                                         case $.jPlayer.event.progress:
 
1713                                                 this._getFlashStatus(status);
 
1714                                                 this._updateInterface();
 
1715                                                 this._trigger(eventType);
 
1717                                         case $.jPlayer.event.timeupdate:
 
1718                                                 this._getFlashStatus(status);
 
1719                                                 this._updateInterface();
 
1720                                                 this._trigger(eventType);
 
1722                                         case $.jPlayer.event.play:
 
1724                                                 this._updateButtons(true);
 
1725                                                 this._trigger(eventType);
 
1727                                         case $.jPlayer.event.pause:
 
1728                                                 this._updateButtons(false);
 
1729                                                 this._trigger(eventType);
 
1731                                         case $.jPlayer.event.ended:
 
1732                                                 this._updateButtons(false);
 
1733                                                 this._trigger(eventType);
 
1735                                         case $.jPlayer.event.click:
 
1736                                                 this._trigger(eventType); // This could be dealt with by the default
 
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'});
 
1744                                                 if(this._validString(this.status.media.poster)) {
 
1745                                                         this.internal.poster.jq.show();
 
1747                                                 if(this.css.jq.videoPlay.length && this.status.video) {
 
1748                                                         this.css.jq.videoPlay.show();
 
1750                                                 if(this.status.video) { // Set up for another try. Execute before error event.
 
1751                                                         this._flash_setVideo(this.status.media);
 
1753                                                         this._flash_setAudio(this.status.media);
 
1755                                                 this._updateButtons(false);
 
1757                                                         type: $.jPlayer.error.URL,
 
1759                                                         message: $.jPlayer.errorMsg.URL,
 
1760                                                         hint: $.jPlayer.errorHint.URL
 
1763                                         case $.jPlayer.event.seeking:
 
1765                                                 this._trigger(eventType);
 
1767                                         case $.jPlayer.event.seeked:
 
1769                                                 this._trigger(eventType);
 
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.
 
1776                                                 this._trigger(eventType);
 
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;
 
1789                         this.status.videoWidth = status.videoWidth;
 
1790                         this.status.videoHeight = status.videoHeight;
 
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;
 
1798                 _updateButtons: function(playing) {
 
1799                         if(playing === undefined) {
 
1800                                 playing = !this.status.paused;
 
1802                                 this.status.paused = !playing;
 
1804                         // Apply the state classes. (For the useStateClassSkin:true option)
 
1806                                 this.addStateClass('playing');
 
1808                                 this.removeStateClass('playing');
 
1810                         if(!this.status.noFullWindow && this.options.fullWindow) {
 
1811                                 this.addStateClass('fullScreen');
 
1813                                 this.removeStateClass('fullScreen');
 
1815                         if(this.options.loop) {
 
1816                                 this.addStateClass('looped');
 
1818                                 this.removeStateClass('looped');
 
1820                         // Toggle the GUI element pairs. (For the useStateClassSkin:false option)
 
1821                         if(this.css.jq.play.length && this.css.jq.pause.length) {
 
1823                                         this.css.jq.play.hide();
 
1824                                         this.css.jq.pause.show();
 
1826                                         this.css.jq.play.show();
 
1827                                         this.css.jq.pause.hide();
 
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();
 
1838                                         this.css.jq.fullScreen.show();
 
1839                                         this.css.jq.restoreScreen.hide();
 
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();
 
1847                                         this.css.jq.repeat.show();
 
1848                                         this.css.jq.repeatOff.hide();
 
1852                 _updateInterface: function() {
 
1853                         if(this.css.jq.seekBar.length) {
 
1854                                 this.css.jq.seekBar.width(this.status.seekPercent+"%");
 
1856                         if(this.css.jq.playBar.length) {
 
1857                                 if(this.options.smoothPlayBar) {
 
1858                                         this.css.jq.playBar.stop().animate({
 
1859                                                 width: this.status.currentPercentAbsolute+"%"
 
1862                                         this.css.jq.playBar.width(this.status.currentPercentRelative+"%");
 
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));
 
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;
 
1879                                         if(typeof this.status.media.duration === 'number') {
 
1880                                                 duration = this.status.media.duration;
 
1881                                                 remaining = duration - this.status.currentTime;
 
1883                                         if(this.options.remainingDuration) {
 
1884                                                 durationText = (remaining > 0 ? '-' : '') + this._convertTime(remaining);
 
1886                                                 durationText = this._convertTime(duration);
 
1889                                 if(durationText !== this.css.jq.duration.text()) {
 
1890                                         this.css.jq.duration.text(durationText);
 
1894                 _convertTime: ConvertTime.prototype.time,
 
1895                 _seeking: function() {
 
1896                         if(this.css.jq.seekBar.length) {
 
1897                                 this.css.jq.seekBar.addClass("jp-seeking-bg");
 
1899                         this.addStateClass('seeking');
 
1901                 _seeked: function() {
 
1902                         if(this.css.jq.seekBar.length) {
 
1903                                 this.css.jq.seekBar.removeClass("jp-seeking-bg");
 
1905                         this.removeStateClass('seeking');
 
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;
 
1913                 _resetActive: function() {
 
1914                         this.html.active = false;
 
1915                         this.aurora.active = false;
 
1916                         this.flash.active = false;
 
1918                 _escapeHtml: function(s) {
 
1919                         return s.split('&').join('&').split('<').join('<').split('>').join('>').split('"').join('"');
 
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;
 
1926                 _absoluteMediaUrls: function(media) {
 
1928                         $.each(media, function(type, url) {
 
1929                                 if(url && self.format[type] && url.substr(0, 5) !== "data:") {
 
1930                                         media[type] = self._qualifyURL(url);
 
1935                 addStateClass: function(state) {
 
1936                         if(this.ancestorJq.length) {
 
1937                                 this.ancestorJq.addClass(this.options.stateClass[state]);
 
1940                 removeStateClass: function(state) {
 
1941                         if(this.ancestorJq.length) {
 
1942                                 this.ancestorJq.removeClass(this.options.stateClass[state]);
 
1945                 setMedia: function(media) {
 
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.
 
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.
 
1959                         this._resetActive();
 
1961                         // Clear the Android Fix.
 
1962                         this.androidFix.setMedia = false;
 
1963                         this.androidFix.play = false;
 
1964                         this.androidFix.pause = false;
 
1966                         // Convert all media URLs to absolute URLs.
 
1967                         media = this._absoluteMediaUrls(media);
 
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';
 
1978                                                                 self.html.video.gate = true;
 
1979                                                                 self._html_setVideo(media);
 
1980                                                                 self.html.active = true;
 
1982                                                                 self.flash.gate = true;
 
1983                                                                 self._flash_setVideo(media);
 
1984                                                                 self.flash.active = true;
 
1986                                                         if(self.css.jq.videoPlay.length) {
 
1987                                                                 self.css.jq.videoPlay.show();
 
1989                                                         self.status.video = true;
 
1992                                                                 self.html.audio.gate = true;
 
1993                                                                 self._html_setAudio(media);
 
1994                                                                 self.html.active = true;
 
1996                                                                 // Setup the Android Fix - Only for HTML audio.
 
1997                                                                 if($.jPlayer.platform.android) {
 
1998                                                                         self.androidFix.setMedia = true;
 
2000                                                         } else if(isAurora) {
 
2001                                                                 self.aurora.gate = true;
 
2002                                                                 self._aurora_setAudio(media);
 
2003                                                                 self.aurora.active = true;
 
2005                                                                 self.flash.gate = true;
 
2006                                                                 self._flash_setAudio(media);
 
2007                                                                 self.flash.active = true;
 
2009                                                         if(self.css.jq.videoPlay.length) {
 
2010                                                                 self.css.jq.videoPlay.hide();
 
2012                                                         self.status.video = false;
 
2016                                                 return false; // Exit $.each
 
2020                                         return false; // Exit $.each
 
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;
 
2033                                                         this.internal.poster.jq.show();
 
2037                                 if(typeof media.title === 'string') {
 
2038                                         if(this.css.jq.title.length) {
 
2039                                                 this.css.jq.title.html(media.title);
 
2041                                         if(this.htmlElement.audio) {
 
2042                                                 this.htmlElement.audio.setAttribute('title', media.title);
 
2044                                         if(this.htmlElement.video) {
 
2045                                                 this.htmlElement.video.setAttribute('title', media.title);
 
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
 
2056                                         type: $.jPlayer.error.NO_SUPPORT,
 
2057                                         context: "{supplied:'" + this.options.supplied + "'}",
 
2058                                         message: $.jPlayer.errorMsg.NO_SUPPORT,
 
2059                                         hint: $.jPlayer.errorHint.NO_SUPPORT
 
2063                 _resetMedia: function() {
 
2064                         this._resetStatus();
 
2065                         this._updateButtons(false);
 
2066                         this._updateInterface();
 
2068                         this.internal.poster.jq.hide();
 
2070                         clearTimeout(this.internal.htmlDlyCmdId);
 
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();
 
2080                 clearMedia: function() {
 
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();
 
2092                         this._resetActive();
 
2095                         if(this.status.srcSet) {
 
2096                                 if(this.html.active) {
 
2098                                 } else if(this.aurora.active) {
 
2099                                         this._aurora_load();
 
2100                                 } else if(this.flash.active) {
 
2104                                 this._urlNotSetError("load");
 
2108                         if(this.options.keyEnabled) {
 
2109                                 $.jPlayer.focus = this;
 
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.
 
2117                                 time = (typeof time === "number") ? time : NaN; // Remove jQuery event from click handler
 
2118                                 if(this.status.srcSet) {
 
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);
 
2128                                         this._urlNotSetError("play");
 
2132                 videoPlay: function() { // Handles clicks on the play button over the video poster
 
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);
 
2146                                 this._urlNotSetError("pause");
 
2149                 tellOthers: function(command, conditions) {
 
2151                                 hasConditions = typeof conditions === 'function',
 
2152                                 args = Array.prototype.slice.call(arguments); // Convert arguments to an Array.
 
2154                         if(typeof command !== 'string') { // Ignore, since no command.
 
2155                                 return; // Return undefined to maintain chaining.
 
2158                                 args.splice(1, 1); // Remove the conditions from the arguments
 
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);
 
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;
 
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);
 
2187                                 this._urlNotSetError("stop");
 
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);
 
2201                                 this._urlNotSetError("playHead");
 
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;
 
2213                 mutedWorker: function(muted) {
 
2214                         this.options.muted = muted;
 
2215                         if(this.html.used) {
 
2216                                 this._html_setProperty('muted', muted);
 
2218                         if(this.aurora.used) {
 
2219                                 this._aurora_mute(muted);
 
2221                         if(this.flash.used) {
 
2222                                 this._flash_mute(muted);
 
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);
 
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) {
 
2237                                 mute = mute === undefined ? true : !!mute;
 
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);
 
2245                 _updateMute: function(mute) {
 
2246                         if(mute === undefined) {
 
2247                                 mute = this.options.muted;
 
2250                                 this.addStateClass('muted');
 
2252                                 this.removeStateClass('muted');
 
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();
 
2259                                         this.css.jq.mute.hide();
 
2260                                         this.css.jq.unmute.show();
 
2262                                         this.css.jq.mute.show();
 
2263                                         this.css.jq.unmute.hide();
 
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;
 
2276                 volumeWorker: function(v) {
 
2277                         v = this._limitValue(v, 0, 1);
 
2278                         this.options.volume = v;
 
2280                         if(this.html.used) {
 
2281                                 this._html_setProperty('volume', v);
 
2283                         if(this.aurora.used) {
 
2284                                 this._aurora_volume(v);
 
2286                         if(this.flash.used) {
 
2287                                 this._flash_volume(v);
 
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);
 
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,
 
2303                                         y = $bar.height() - e.pageY + offset.top,
 
2305                                 if(this.options.verticalVolume) {
 
2311                         if(this.options.muted) {
 
2315                 _updateVolume: function(v) {
 
2316                         if(v === undefined) {
 
2317                                 v = this.options.volume;
 
2319                         v = this.options.muted ? 0 : v;
 
2321                         if(this.status.noVolume) {
 
2322                                 this.addStateClass('noVolume');
 
2323                                 if(this.css.jq.volumeBar.length) {
 
2324                                         this.css.jq.volumeBar.hide();
 
2326                                 if(this.css.jq.volumeBarValue.length) {
 
2327                                         this.css.jq.volumeBarValue.hide();
 
2329                                 if(this.css.jq.volumeMax.length) {
 
2330                                         this.css.jq.volumeMax.hide();
 
2333                                 this.removeStateClass('noVolume');
 
2334                                 if(this.css.jq.volumeBar.length) {
 
2335                                         this.css.jq.volumeBar.show();
 
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)+"%");
 
2341                                 if(this.css.jq.volumeMax.length) {
 
2342                                         this.css.jq.volumeMax.show();
 
2346                 volumeMax: function() { // Handles clicks on the volume max
 
2348                         if(this.options.muted) {
 
2352                 _cssSelectorAncestor: function(ancestor) {
 
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.
 
2359                                         type: $.jPlayer.warning.CSS_SELECTOR_COUNT,
 
2361                                         message: $.jPlayer.warningMsg.CSS_SELECTOR_COUNT + this.ancestorJq.length + " found for cssSelectorAncestor.",
 
2362                                         hint: $.jPlayer.warningHint.CSS_SELECTOR_COUNT
 
2366                         $.each(this.options.cssSelector, function(fn, cssSel) {
 
2367                                 self._cssSelector(fn, cssSel);
 
2370                         // Set the GUI to the current state.
 
2371                         this._updateInterface();
 
2372                         this._updateButtons();
 
2373                         this._updateAutohide();
 
2374                         this._updateVolume();
 
2377                 _cssSelector: function(fn, cssSel) {
 
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");
 
2384                                         this.options.cssSelector[fn] = cssSel;
 
2385                                         this.css.cs[fn] = this.options.cssSelectorAncestor + " " + cssSel;
 
2387                                         if(cssSel) { // Checks for empty string
 
2388                                                 this.css.jq[fn] = $(this.css.cs[fn]);
 
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. 
 
2393                                         if(this.css.jq[fn].length && this[fn]) {
 
2394                                                 var handler = function(e) {
 
2397                                                         if(self.options.autoBlur) {
 
2400                                                                 $(this).focus(); // Force focus for ARIA.
 
2403                                                 this.css.jq[fn].bind("click.jPlayer", handler); // Using jPlayer namespace
 
2406                                         if(cssSel && this.css.jq[fn].length !== 1) { // So empty strings do not generate the warning. ie., they just remove the old one.
 
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
 
2416                                                 type: $.jPlayer.warning.CSS_SELECTOR_METHOD,
 
2418                                                 message: $.jPlayer.warningMsg.CSS_SELECTOR_METHOD,
 
2419                                                 hint: $.jPlayer.warningHint.CSS_SELECTOR_METHOD
 
2424                                         type: $.jPlayer.warning.CSS_SELECTOR_STRING,
 
2426                                         message: $.jPlayer.warningMsg.CSS_SELECTOR_STRING,
 
2427                                         hint: $.jPlayer.warningHint.CSS_SELECTOR_STRING
 
2431                 duration: function(e) {
 
2432                         if(this.options.toggleDuration) {
 
2433                                 if(this.options.captureDuration) {
 
2434                                         e.stopPropagation();
 
2436                                 this._setOption("remainingDuration", !this.options.remainingDuration);
 
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,
 
2450                 playbackRate: function(pbr) {
 
2451                         this._setOption("playbackRate", pbr);
 
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,
 
2460                                         y = $bar.height() - e.pageY + offset.top,
 
2463                                 if(this.options.verticalPlaybackRate) {
 
2468                                 pbr = ratio * (this.options.maxPlaybackRate - this.options.minPlaybackRate) + this.options.minPlaybackRate;
 
2469                                 this.playbackRate(pbr);
 
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();
 
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)+"%");
 
2484                                 if(this.css.jq.playbackRateBar.length) {
 
2485                                         this.css.jq.playbackRateBar.hide();
 
2487                                 if(this.css.jq.playbackRateBarValue.length) {
 
2488                                         this.css.jq.playbackRateBarValue.hide();
 
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) {
 
2500                 repeatOff: function() { // Handle clicks on the repeatOff button
 
2503                 _loop: function(loop) {
 
2504                         if(this.options.loop !== loop) {
 
2505                                 this.options.loop = loop;
 
2506                                 this._updateButtons();
 
2507                                 this._trigger($.jPlayer.event.repeat);
 
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) {
 
2515                          // Enables use: options().  Returns a copy of options object
 
2516                         if ( arguments.length === 0 ) {
 
2517                                 return $.extend( true, {}, this.options );
 
2520                         if(typeof key === "string") {
 
2521                                 var keys = key.split(".");
 
2523                                  // Enables use: options("someOption")  Returns a copy of the option. Supports dot notation.
 
2524                                 if(value === undefined) {
 
2526                                         var opt = $.extend(true, {}, this.options);
 
2527                                         for(var i = 0; i < keys.length; i++) {
 
2528                                                 if(opt[keys[i]] !== undefined) {
 
2532                                                                 type: $.jPlayer.warning.OPTION_KEY,
 
2534                                                                 message: $.jPlayer.warningMsg.OPTION_KEY,
 
2535                                                                 hint: $.jPlayer.warningHint.OPTION_KEY
 
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}}
 
2550                                 for(var j = 0; j < keys.length; j++) {
 
2551                                         if(j < keys.length - 1) {
 
2553                                                 opts = opts[keys[j]];
 
2555                                                 opts[keys[j]] = value;
 
2560                          // Otherwise enables use: options(optionObject).  Uses original object (the key)
 
2562                         this._setOptions(options);
 
2566                 _setOptions: function(options) {
 
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);
 
2574                 _setOption: function(key, value) {
 
2577                         // The ability to set options is limited at this time.
 
2586                                 case "globalVolume" :
 
2587                                         this.options[key] = value;
 
2589                                 case "cssSelectorAncestor" :
 
2590                                         this._cssSelectorAncestor(value); // Set and refresh all associations for the new ancestor.
 
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.
 
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);
 
2602                                         this._updatePlaybackRate();
 
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);
 
2609                                         this._updatePlaybackRate();
 
2611                                 case "minPlaybackRate" :
 
2612                                         this.options[key] = value = this._limitValue(value, 0.1, this.options.maxPlaybackRate - 0.1);
 
2613                                         this._updatePlaybackRate();
 
2615                                 case "maxPlaybackRate" :
 
2616                                         this.options[key] = value = this._limitValue(value, this.options.minPlaybackRate + 0.1, 16);
 
2617                                         this._updatePlaybackRate();
 
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;
 
2627                                                                 this._requestFullscreen();
 
2629                                                                 this._exitFullscreen();
 
2632                                                                 this._setOption("fullWindow", value);
 
2638                                         if(this.options[key] !== value) { // if changed
 
2639                                                 this._removeUiClass();
 
2640                                                 this.options[key] = value;
 
2641                                                 this._refreshSize();
 
2645                                         if(!this.options.fullWindow && this.options[key].cssClass !== value.cssClass) {
 
2646                                                 this._removeUiClass();
 
2648                                         this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed.
 
2649                                         this._refreshSize();
 
2652                                         if(this.options.fullWindow && this.options[key].cssClass !== value.cssClass) {
 
2653                                                 this._removeUiClass();
 
2655                                         this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed.
 
2656                                         this._refreshSize();
 
2659                                         this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed.
 
2660                                         this._updateAutohide();
 
2665                                 case "remainingDuration" :
 
2666                                         this.options[key] = value;
 
2667                                         this._updateInterface();
 
2669                                 case "toggleDuration" :
 
2670                                         this.options[key] = value;
 
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();
 
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();
 
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();
 
2691                                 case "emulateHtml" :
 
2692                                         if(this.options[key] !== value) { // To avoid multiple event handlers being created, if true already.
 
2693                                                 this.options[key] = value;
 
2695                                                         this._emulateHtmlBridge();
 
2697                                                         this._destroyHtmlBridge();
 
2702                                         this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed.
 
2705                                         this.options[key] = value;
 
2706                                         if(!value && this === $.jPlayer.focus) {
 
2707                                                 $.jPlayer.focus = null;
 
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.
 
2713                                 case "audioFullScreen" :
 
2714                                         this.options[key] = value;
 
2717                                         this.options[key] = value;
 
2723                 // End of: (Options code adapted from ui.widget.js)
 
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);
 
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;
 
2740                                 this.status.width = this.options.size.width;
 
2741                                 this.status.height = this.options.size.height;
 
2742                                 this.status.cssClass = this.options.size.cssClass;
 
2745                         // Set the size of the jPlayer area.
 
2746                         this.element.css({'width': this.status.width, 'height': this.status.height});
 
2748                 _addUiClass: function() {
 
2749                         if(this.ancestorJq.length) {
 
2750                                 this.ancestorJq.addClass(this.status.cssClass);
 
2753                 _removeUiClass: function() {
 
2754                         if(this.ancestorJq.length) {
 
2755                                 this.ancestorJq.removeClass(this.status.cssClass);
 
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});
 
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});
 
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});
 
2770                 _updateAutohide: function() {
 
2772                                 event = "mousemove.jPlayer",
 
2773                                 namespace = ".jPlayerAutohide",
 
2774                                 eventType = event + namespace,
 
2775                                 handler = function(event) {
 
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); 
 
2786                                         // store current position for next method execution
 
2787                                         self.internal.mouse = {
 
2791                                         // if mouse has been actually moved, do the gui fadeIn/fadeOut
 
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);
 
2802                         if(this.css.jq.gui.length) {
 
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);
 
2808                                 // Removes the fadeOut operation from the fadeIn callback.
 
2809                                 clearTimeout(this.internal.autohideId);
 
2811                                 delete this.internal.mouse;
 
2813                                 this.element.unbind(namespace);
 
2814                                 this.css.jq.gui.unbind(namespace);
 
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();
 
2822                                                 this.css.jq.gui.show();
 
2825                                         this.css.jq.gui.hide();
 
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);
 
2834                                 this._setOption("fullScreen", true);
 
2837                 restoreScreen: function() {
 
2838                         this._setOption("fullScreen", false);
 
2840                 _fullscreenAddEventListeners: function() {
 
2842                                 fs = $.jPlayer.nativeFeatures.fullscreen;
 
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();
 
2852                                         document.addEventListener(fs.event.fullscreenchange, this.internal.fullscreenchangeHandler, false);
 
2854                                 // No point creating handler for fullscreenerror.
 
2855                                 // Either logic avoids fullscreen occurring (w3c/moz), or their is no event on the browser (webkit).
 
2858                 _fullscreenRemoveEventListeners: function() {
 
2859                         var fs = $.jPlayer.nativeFeatures.fullscreen;
 
2860                         if(this.internal.fullscreenchangeHandler) {
 
2861                                 document.removeEventListener(fs.event.fullscreenchange, this.internal.fullscreenchangeHandler, false);
 
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);
 
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;
 
2875                         // This method needs the video element. For iOS and Android.
 
2876                         if(fs.used.webkitVideo) {
 
2877                                 e = this.htmlElement.video;
 
2880                         if(fs.api.fullscreenEnabled) {
 
2881                                 fs.api.requestFullscreen(e);
 
2884                 _exitFullscreen: function() {
 
2886                         var fs = $.jPlayer.nativeFeatures.fullscreen,
 
2889                         // This method needs the video element. For iOS and Android.
 
2890                         if(fs.used.webkitVideo) {
 
2891                                 e = this.htmlElement.video;
 
2894                         if(fs.api.fullscreenEnabled) {
 
2895                                 fs.api.exitFullscreen(e);
 
2898                 _html_initMedia: function(media) {
 
2899                         // Remove any existing track elements
 
2900                         var $media = $(this.htmlElement.media).empty();
 
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 : "");
 
2910                                         track.setAttribute("default", v.def);
 
2912                                 $media.append(track);
 
2915                         this.htmlElement.media.src = this.status.src;
 
2917                         if(this.options.preload !== 'none') {
 
2918                                 this._html_load(); // See function for comments
 
2920                         this._trigger($.jPlayer.event.timeupdate); // The flash generates this event for its solution.
 
2922                 _html_setFormat: function(media) {
 
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;
 
2934                 _html_setAudio: function(media) {
 
2935                         this._html_setFormat(media);
 
2936                         this.htmlElement.media = this.htmlElement.audio;
 
2937                         this._html_initMedia(media);
 
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 : "";
 
2944                         this.htmlElement.media = this.htmlElement.video;
 
2945                         this._html_initMedia(media);
 
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'});
 
2952                                 this.htmlElement.media.pause();
 
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.
 
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();
 
2971                         clearTimeout(this.internal.htmlDlyCmdId);
 
2973                 _html_play: function(time) {
 
2975                                 media = this.htmlElement.media;
 
2977                         this.androidFix.pause = false; // Cancel the pause fix.
 
2979                         this._html_load(); // Loads if required and clears any delayed commands.
 
2981                         // Setup the Android Fix.
 
2982                         if(this.androidFix.setMedia) {
 
2983                                 this.androidFix.play = true;
 
2984                                 this.androidFix.time = time;
 
2986                         } else if(!isNaN(time)) {
 
2988                                 // Attempt to play it, since iOS has been ignoring commands
 
2989                                 if(this.internal.cmdsIgnored) {
 
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;
 
3003                                         this.internal.htmlDlyCmdId = setTimeout(function() {
 
3006                                         return; // Cancel execution and wait for the delayed command.
 
3011                         this._html_checkWaitForPlay();
 
3013                 _html_pause: function(time) {
 
3015                                 media = this.htmlElement.media;
 
3017                         this.androidFix.play = false; // Cancel the play fix.
 
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.
 
3022                                 clearTimeout(this.internal.htmlDlyCmdId);
 
3025                         // Order of these commands is important for Safari (Win) and IE9. Pause then change currentTime.
 
3028                         // Setup the Android Fix.
 
3029                         if(this.androidFix.setMedia) {
 
3030                                 this.androidFix.pause = true;
 
3031                                 this.androidFix.time = time;
 
3033                         } else if(!isNaN(time)) {
 
3035                                         if(!media.seekable || typeof media.seekable === "object" && media.seekable.length > 0) {
 
3036                                                 media.currentTime = time;
 
3041                                         this.internal.htmlDlyCmdId = setTimeout(function() {
 
3044                                         return; // Cancel execution and wait for the delayed command.
 
3047                         if(time > 0) { // Avoids a setMedia() followed by stop() or pause(0) hiding the video play button.
 
3048                                 this._html_checkWaitForPlay();
 
3051                 _html_playHead: function(percent) {
 
3053                                 media = this.htmlElement.media;
 
3055                         this._html_load(); // Loads if required and clears any delayed commands.
 
3057                         // This playHead() method needs a refactor to apply the android fix.
 
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;
 
3068                                 this.internal.htmlDlyCmdId = setTimeout(function() {
 
3069                                         self.playHead(percent);
 
3071                                 return; // Cancel execution and wait for the delayed command.
 
3073                         if(!this.status.waitForLoad) {
 
3074                                 this._html_checkWaitForPlay();
 
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();
 
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});
 
3089                 _html_setProperty: function(property, value) {
 
3090                         if(this.html.audio.available) {
 
3091                                 this.htmlElement.audio[property] = value;
 
3093                         if(this.html.video.available) {
 
3094                                 this.htmlElement.video[property] = value;
 
3097                 _aurora_setAudio: function(media) {
 
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;
 
3111                         this.aurora.player = new AV.Player.fromURL(this.status.src);
 
3112                         this._addAuroraEventListeners(this.aurora.player, this.aurora);
 
3114                         if(this.options.preload === 'auto') {
 
3115                                 this._aurora_load();
 
3116                                 this.status.waitForLoad = false;
 
3119                 _aurora_resetMedia: function() {
 
3120                         if (this.aurora.player) {
 
3121                                 this.aurora.player.stop();
 
3124                 _aurora_clearMedia: function() {
 
3125                         // Nothing to clear.
 
3127                 _aurora_load: function() {
 
3128                         if(this.status.waitForLoad) {
 
3129                                 this.status.waitForLoad = false;
 
3130                                 this.aurora.player.preload();
 
3133                 _aurora_play: function(time) {
 
3134                         if (!this.status.waitForLoad) {
 
3136                                         this.aurora.player.seek(time);
 
3139                         if (!this.aurora.player.playing) {
 
3140                                 this.aurora.player.play();
 
3142                         this.status.waitForLoad = false;
 
3143                         this._aurora_checkWaitForPlay();
 
3145                         // No event from the player, update UI now.
 
3146                         this._updateButtons(true);
 
3147                         this._trigger($.jPlayer.event.play);
 
3149                 _aurora_pause: function(time) {
 
3151                                 this.aurora.player.seek(time * 1000);
 
3153                         this.aurora.player.pause();
 
3155                         if(time > 0) { // Avoids a setMedia() followed by stop() or pause(0) hiding the video play button.
 
3156                                 this._aurora_checkWaitForPlay();
 
3159                         // No event from the player, update UI now.
 
3160                         this._updateButtons(false);
 
3161                         this._trigger($.jPlayer.event.pause);
 
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
 
3169                         if(!this.status.waitForLoad) {
 
3170                                 this._aurora_checkWaitForPlay();
 
3173                 _aurora_checkWaitForPlay: function() {
 
3174                         if(this.status.waitForPlay) {
 
3175                                 this.status.waitForPlay = false;
 
3178                 _aurora_volume: function(v) {
 
3179                         this.aurora.player.volume = v * 100;
 
3181                 _aurora_mute: function(m) {
 
3183                                 this.aurora.properties.lastvolume = this.aurora.player.volume;
 
3184                                 this.aurora.player.volume = 0;
 
3186                                 this.aurora.player.volume = this.aurora.properties.lastvolume;
 
3188                         this.aurora.properties.muted = m;
 
3190                 _flash_setAudio: function(media) {
 
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]) {
 
3199                                                                 self._getMovie().fl_setAudio_m4a(media[format]);
 
3202                                                                 self._getMovie().fl_setAudio_mp3(media[format]);
 
3205                                                                 self._getMovie().fl_setAudio_rtmp(media[format]);
 
3208                                                 self.status.src = media[format];
 
3209                                                 self.status.format[format] = true;
 
3210                                                 self.status.formatType = format;
 
3215                                 if(this.options.preload === 'auto') {
 
3217                                         this.status.waitForLoad = false;
 
3219                         } catch(err) { this._flashError(err); }
 
3221                 _flash_setVideo: function(media) {
 
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]) {
 
3230                                                                 self._getMovie().fl_setVideo_m4v(media[format]);
 
3233                                                                 self._getMovie().fl_setVideo_rtmp(media[format]);
 
3236                                                 self.status.src = media[format];
 
3237                                                 self.status.format[format] = true;
 
3238                                                 self.status.formatType = format;
 
3243                                 if(this.options.preload === 'auto') {
 
3245                                         this.status.waitForLoad = false;
 
3247                         } catch(err) { this._flashError(err); }
 
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);
 
3253                 _flash_clearMedia: function() {
 
3255                                 this._getMovie().fl_clearMedia();
 
3256                         } catch(err) { this._flashError(err); }
 
3258                 _flash_load: function() {
 
3260                                 this._getMovie().fl_load();
 
3261                         } catch(err) { this._flashError(err); }
 
3262                         this.status.waitForLoad = false;
 
3264                 _flash_play: function(time) {
 
3266                                 this._getMovie().fl_play(time);
 
3267                         } catch(err) { this._flashError(err); }
 
3268                         this.status.waitForLoad = false;
 
3269                         this._flash_checkWaitForPlay();
 
3271                 _flash_pause: function(time) {
 
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();
 
3280                 _flash_playHead: function(p) {
 
3282                                 this._getMovie().fl_play_head(p);
 
3283                         } catch(err) { this._flashError(err); }
 
3284                         if(!this.status.waitForLoad) {
 
3285                                 this._flash_checkWaitForPlay();
 
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();
 
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});
 
3300                 _flash_volume: function(v) {
 
3302                                 this._getMovie().fl_volume(v);
 
3303                         } catch(err) { this._flashError(err); }
 
3305                 _flash_mute: function(m) {
 
3307                                 this._getMovie().fl_mute(m);
 
3308                         } catch(err) { this._flashError(err); }
 
3310                 _getMovie: function() {
 
3311                         return document[this.internal.flash.id];
 
3313                 _getFlashPluginVersion: function() {
 
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/
 
3321                         if(window.ActiveXObject) {
 
3323                                         flash = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
 
3324                                         if (flash) { // flash will return null when ActiveX is disabled
 
3325                                                 var v = flash.GetVariable("$version");
 
3327                                                         v = v.split(" ")[1].split(",");
 
3328                                                         version = parseInt(v[0], 10) + "." + parseInt(v[1], 10);
 
3333                         else if(navigator.plugins && navigator.mimeTypes.length > 0) {
 
3334                                 flash = navigator.plugins["Shockwave Flash"];
 
3336                                         version = navigator.plugins["Shockwave Flash"].description.replace(/.*\s(\d+\.\d+).*/, "$1");
 
3339                         return version * 1; // Converts to a number
 
3341                 _checkForFlash: function (version) {
 
3342                         var flashOk = false;
 
3343                         if(this._getFlashPluginVersion() >= version) {
 
3348                 _validString: function(url) {
 
3349                         return (url && typeof url === "string"); // Empty strings return false
 
3351                 _limitValue: function(value, min, max) {
 
3352                         return (value < min) ? min : ((value > max) ? max : value);
 
3354                 _urlNotSetError: function(context) {
 
3356                                 type: $.jPlayer.error.URL_NOT_SET,
 
3358                                 message: $.jPlayer.errorMsg.URL_NOT_SET,
 
3359                                 hint: $.jPlayer.errorHint.URL_NOT_SET
 
3362                 _flashError: function(error) {
 
3364                         if(!this.internal.ready) {
 
3365                                 errorType = "FLASH";
 
3367                                 errorType = "FLASH_DISABLED";
 
3370                                 type: $.jPlayer.error[errorType],
 
3371                                 context: this.internal.flash.swf,
 
3372                                 message: $.jPlayer.errorMsg[errorType] + error.message,
 
3373                                 hint: $.jPlayer.errorHint[errorType]
 
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'});
 
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);
 
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);
 
3391                 _alert: function(message) {
 
3392                         var msg = "jPlayer " + this.version.script + " : id='" + this.internal.self.id +"' : " + message;
 
3393                         if(!this.options.consoleAlerts) {
 
3395                         } else if(window.console && window.console.log) {
 
3396                                 window.console.log(msg);
 
3399                 _emulateHtmlBridge: function() {
 
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) {
 
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;
 
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);
 
3427                                 // The error event would require a special case
 
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.
 
3432                 _emulateHtmlUpdate: function() {
 
3435                         $.each( $.jPlayer.emulateStatus.split(/\s+/g), function(i, name) {
 
3436                                 self.internal.domNode[name] = self.status[name];
 
3438                         $.each( $.jPlayer.emulateOptions.split(/\s+/g), function(i, name) {
 
3439                                 self.internal.domNode[name] = self.options[name];
 
3442                 _destroyHtmlBridge: function() {
 
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.
 
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];
 
3458                 FLASH_DISABLED: "e_flash_disabled",
 
3459                 NO_SOLUTION: "e_no_solution",
 
3460                 NO_SUPPORT: "e_no_support",
 
3462                 URL_NOT_SET: "e_url_not_set",
 
3463                 VERSION: "e_version"
 
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()
 
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."
 
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"
 
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."
 
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."