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."