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"
23 (function (root, factory) {
24 if (typeof define === 'function' && define.amd) {
25 // AMD. Register as an anonymous module.
26 define(['jquery'], factory); // jQuery Switch
27 // define(['zepto'], factory); // Zepto Switch
28 } else if (typeof exports === 'object') {
30 factory(require('jquery')); // jQuery Switch
31 //factory(require('zepto')); // Zepto Switch
34 if(root.jQuery) { // Use jQuery if available
36 } else { // Otherwise, use Zepto
40 }(this, function ($, undefined) {
42 // Adapted from jquery.ui.widget.js (1.8.7): $.widget.bridge - Tweaked $.data(this,XYZ) to $(this).data(XYZ) for Zepto
43 $.fn.jPlayer = function( options ) {
45 var isMethodCall = typeof options === "string",
46 args = Array.prototype.slice.call( arguments, 1 ),
49 // allow multiple hashes to be passed on init
50 options = !isMethodCall && args.length ?
51 $.extend.apply( null, [ true, options ].concat(args) ) :
54 // prevent calls to internal methods
55 if ( isMethodCall && options.charAt( 0 ) === "_" ) {
60 this.each(function() {
61 var instance = $(this).data( name ),
62 methodValue = instance && $.isFunction( instance[options] ) ?
63 instance[ options ].apply( instance, args ) :
65 if ( methodValue !== instance && methodValue !== undefined ) {
66 returnValue = methodValue;
71 this.each(function() {
72 var instance = $(this).data( name );
74 // instance.option( options || {} )._init(); // Orig jquery.ui.widget.js code: Not recommend for jPlayer. ie., Applying new options to an existing instance (via the jPlayer constructor) and performing the _init(). The _init() is what concerns me. It would leave a lot of event handlers acting on jPlayer instance and the interface.
75 instance.option( options || {} ); // The new constructor only changes the options. Changing options only has basic support atm.
77 $(this).data( name, new $.jPlayer( options, this ) );
85 $.jPlayer = function( options, element ) {
86 // allow instantiation without initializing for simple inheritance
87 if ( arguments.length ) {
88 this.element = $(element);
89 this.options = $.extend(true, {},
94 this.element.bind( "remove.jPlayer", function() {
100 // End of: (Adapted from jquery.ui.widget.js (1.8.7))
102 // Zepto is missing one of the animation methods.
103 if(typeof $.fn.stop !== 'function') {
104 $.fn.stop = function() {};
107 // Emulated HTML5 methods and properties
108 $.jPlayer.emulateMethods = "load play pause";
109 $.jPlayer.emulateStatus = "src readyState networkState currentTime duration paused ended playbackRate";
110 $.jPlayer.emulateOptions = "muted volume";
112 // Reserved event names generated by jPlayer that are not part of the HTML5 Media element spec
113 $.jPlayer.reservedEvent = "ready flashreset resize repeat error warning";
115 // Events generated by jPlayer
116 $.jPlayer.event = {};
120 'setmedia', // Fires when the media is set
121 'flashreset', // Similar to the ready event if the Flash solution is set to display:none and then shown again or if it's reloaded for another reason by the browser. For example, using CSS position:fixed on Firefox for the full screen feature.
122 'resize', // Occurs when the size changes through a full/restore screen operation or if the size/sizeFull options are changed.
123 'repeat', // Occurs when the repeat status changes. Usually through clicks on the repeat button of the interface.
124 'click', // Occurs when the user clicks on one of the following: poster image, html video, flash video.
125 'error', // Event error code in event.jPlayer.error.type. See $.jPlayer.error
126 'warning', // Event warning code in event.jPlayer.warning.type. See $.jPlayer.warning
128 // Other events match HTML5 spec.
152 $.jPlayer.event[ this ] = 'jPlayer_' + this;
156 $.jPlayer.htmlEvent = [ // These HTML events are bubbled through to the jPlayer event, without any internal action.
158 // "progress", // jPlayer uses internally before bubbling.
159 // "suspend", // jPlayer uses internally before bubbling.
161 // "error", // jPlayer uses internally before bubbling.
164 // "play", // jPlayer uses internally before bubbling.
165 // "pause", // jPlayer uses internally before bubbling.
167 // "loadeddata", // jPlayer uses internally before bubbling.
168 // "waiting", // jPlayer uses internally before bubbling.
169 // "playing", // jPlayer uses internally before bubbling.
172 // "seeking", // jPlayer uses internally before bubbling.
173 // "seeked", // jPlayer uses internally before bubbling.
174 // "timeupdate", // jPlayer uses internally before bubbling.
175 // "ended", // jPlayer uses internally before bubbling.
176 // "ratechange" // jPlayer uses internally before bubbling.
177 // "durationchange" // jPlayer uses internally before bubbling.
178 // "volumechange" // jPlayer uses internally before bubbling.
181 $.jPlayer.pause = function() {
182 $.jPlayer.prototype.destroyRemoved();
183 $.each($.jPlayer.prototype.instances, function(i, element) {
184 if(element.data("jPlayer").status.srcSet) { // Check that media is set otherwise would cause error event.
185 element.jPlayer("pause");
190 // Default for jPlayer option.timeFormat
191 $.jPlayer.timeFormat = {
202 var ConvertTime = function() {
205 ConvertTime.prototype = {
208 timeFormat: $.jPlayer.timeFormat
211 time: function(s) { // function used on jPlayer.prototype._convertTime to enable per instance options.
212 s = (s && typeof s === 'number') ? s : 0;
214 var myTime = new Date(s * 1000),
215 hour = myTime.getUTCHours() + (myTime.getDate() - 1) * 24,
216 min = this.options.timeFormat.showHour ? myTime.getUTCMinutes() : myTime.getUTCMinutes() + hour * 60,
217 sec = this.options.timeFormat.showMin ? myTime.getUTCSeconds() : myTime.getUTCSeconds() + min * 60,
218 strHour = (this.options.timeFormat.padHour && hour < 10) ? "0" + hour : hour,
219 strMin = (this.options.timeFormat.padMin && min < 10) ? "0" + min : min,
220 strSec = (this.options.timeFormat.padSec && sec < 10) ? "0" + sec : sec,
223 strTime += this.options.timeFormat.showHour ? strHour + this.options.timeFormat.sepHour : "";
224 strTime += this.options.timeFormat.showMin ? strMin + this.options.timeFormat.sepMin : "";
225 strTime += this.options.timeFormat.showSec ? strSec + this.options.timeFormat.sepSec : "";
230 var myConvertTime = new ConvertTime();
231 $.jPlayer.convertTime = function(s) {
232 return myConvertTime.time(s);
235 // Adapting jQuery 1.4.4 code for jQuery.browser. Required since jQuery 1.3.2 does not detect Chrome as webkit.
236 $.jPlayer.uaBrowser = function( userAgent ) {
237 var ua = userAgent.toLowerCase();
240 var rwebkit = /(webkit)[ \/]([\w.]+)/;
241 var ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/;
242 var rmsie = /(msie) ([\w.]+)/;
243 var rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/;
245 var match = rwebkit.exec( ua ) ||
248 ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
251 return { browser: match[1] || "", version: match[2] || "0" };
254 // Platform sniffer for detecting mobile devices
255 $.jPlayer.uaPlatform = function( userAgent ) {
256 var ua = userAgent.toLowerCase();
259 var rplatform = /(ipad|iphone|ipod|android|blackberry|playbook|windows ce|webos)/;
260 var rtablet = /(ipad|playbook)/;
261 var randroid = /(android)/;
262 var rmobile = /(mobile)/;
264 var platform = rplatform.exec( ua ) || [];
265 var tablet = rtablet.exec( ua ) ||
266 !rmobile.exec( ua ) && randroid.exec( ua ) ||
270 platform[1] = platform[1].replace(/\s/g, "_"); // Change whitespace to underscore. Enables dot notation.
273 return { platform: platform[1] || "", tablet: tablet[1] || "" };
276 $.jPlayer.browser = {
278 $.jPlayer.platform = {
281 var browserMatch = $.jPlayer.uaBrowser(navigator.userAgent);
282 if ( browserMatch.browser ) {
283 $.jPlayer.browser[ browserMatch.browser ] = true;
284 $.jPlayer.browser.version = browserMatch.version;
286 var platformMatch = $.jPlayer.uaPlatform(navigator.userAgent);
287 if ( platformMatch.platform ) {
288 $.jPlayer.platform[ platformMatch.platform ] = true;
289 $.jPlayer.platform.mobile = !platformMatch.tablet;
290 $.jPlayer.platform.tablet = !!platformMatch.tablet;
293 // Internet Explorer (IE) Browser Document Mode Sniffer. Based on code at:
294 // http://msdn.microsoft.com/en-us/library/cc288325%28v=vs.85%29.aspx#GetMode
295 $.jPlayer.getDocMode = function() {
297 if ($.jPlayer.browser.msie) {
298 if (document.documentMode) { // IE8 or later
299 docMode = document.documentMode;
301 docMode = 5; // Assume quirks mode unless proven otherwise
302 if (document.compatMode) {
303 if (document.compatMode === "CSS1Compat") {
304 docMode = 7; // standards mode
311 $.jPlayer.browser.documentMode = $.jPlayer.getDocMode();
313 $.jPlayer.nativeFeatures = {
316 /* Fullscreen function naming influenced by W3C naming.
317 * No support for: Mozilla Proposal: https://wiki.mozilla.org/Gecko:FullScreenAPI
321 v = d.createElement('video'),
323 // http://www.w3.org/TR/fullscreen/
332 // https://developer.mozilla.org/en-US/docs/DOM/Using_fullscreen_mode
334 'mozFullScreenEnabled',
335 'mozFullScreenElement',
336 'mozRequestFullScreen',
337 'mozCancelFullScreen',
338 'mozfullscreenchange',
341 // http://developer.apple.com/library/safari/#documentation/WebKit/Reference/ElementClassRef/Element/Element.html
342 // http://developer.apple.com/library/safari/#documentation/UserExperience/Reference/DocumentAdditionsReference/DocumentAdditions/DocumentAdditions.html
345 'webkitCurrentFullScreenElement',
346 'webkitRequestFullScreen',
347 'webkitCancelFullScreen',
348 'webkitfullscreenchange',
351 // http://developer.apple.com/library/safari/#documentation/AudioVideo/Reference/HTMLVideoElementClassReference/HTMLVideoElement/HTMLVideoElement.html
352 // https://developer.apple.com/library/safari/samplecode/HTML5VideoEventFlow/Listings/events_js.html#//apple_ref/doc/uid/DTS40010085-events_js-DontLinkElementID_5
353 // Events: 'webkitbeginfullscreen' and 'webkitendfullscreen'
355 'webkitSupportsFullscreen',
356 'webkitDisplayingFullscreen',
357 'webkitEnterFullscreen',
358 'webkitExitFullscreen',
364 'msFullscreenElement',
365 'msRequestFullscreen',
367 'MSFullscreenChange',
380 this.fullscreen = fs = {
382 w3c: !!d[spec.w3c[0]],
383 moz: !!d[spec.moz[0]],
384 webkit: typeof d[spec.webkit[3]] === 'function',
385 webkitVideo: typeof v[spec.webkitVideo[2]] === 'function',
386 ms: typeof v[spec.ms[2]] === 'function'
391 // Store the name of the spec being used and as a handy boolean.
392 for(i = 0, il = specOrder.length; i < il; i++) {
393 var n = specOrder[i];
402 var s = spec[fs.spec];
404 fullscreenEnabled: true,
405 fullscreenElement: function(elem) {
406 elem = elem ? elem : d; // Video element required for webkitVideo
409 requestFullscreen: function(elem) {
410 return elem[s[2]](); // Chrome and Opera want parameter (Element.ALLOW_KEYBOARD_INPUT) but Safari fails if flag used.
412 exitFullscreen: function(elem) {
413 elem = elem ? elem : d; // Video element required for webkitVideo
418 fullscreenchange: s[4],
419 fullscreenerror: s[5]
423 fullscreenEnabled: false,
424 fullscreenElement: function() {
427 requestFullscreen: function() {},
428 exitFullscreen: function() {}
434 $.jPlayer.nativeFeatures.init();
436 // The keyboard control system.
438 // The current jPlayer instance in focus.
439 $.jPlayer.focus = null;
441 // The list of element node names to ignore with key controls.
442 $.jPlayer.keyIgnoreElementNames = "A INPUT TEXTAREA SELECT BUTTON";
444 // The function that deals with key presses.
445 var keyBindings = function(event) {
446 var f = $.jPlayer.focus,
449 // A jPlayer instance must be in focus. ie., keyEnabled and the last one played.
451 // What generated the key press?
452 $.each( $.jPlayer.keyIgnoreElementNames.split(/\s+/g), function(i, name) {
453 // The strings should already be uppercase.
454 if(event.target.nodeName.toUpperCase() === name.toUpperCase()) {
456 return false; // exit each.
460 // See if the key pressed matches any of the bindings.
461 $.each(f.options.keyBindings, function(action, binding) {
462 // The binding could be a null when the default has been disabled. ie., 1st clause in if()
464 (binding && $.isFunction(binding.fn)) &&
465 ((typeof binding.key === 'number' && event.which === binding.key) ||
466 (typeof binding.key === 'string' && event.key === binding.key))
468 event.preventDefault(); // Key being used by jPlayer, so prevent default operation.
470 return false; // exit each.
477 $.jPlayer.keys = function(en) {
478 var event = "keydown.jPlayer";
479 // Remove any binding, just in case enabled more than once.
480 $(document.documentElement).unbind(event);
482 $(document.documentElement).bind(event, keyBindings);
486 // Enable the global key control handler ready for any jPlayer instance with the keyEnabled option enabled.
487 $.jPlayer.keys(true);
489 $.jPlayer.prototype = {
490 count: 0, // Static Variable: Change it via prototype.
491 version: { // Static Object
496 options: { // Instanced in $.jPlayer() constructor
497 swfPath: "js", // Path to jquery.jplayer.swf. Can be relative, absolute or server root relative.
498 solution: "html, flash", // Valid solutions: html, flash, aurora. Order defines priority. 1st is highest,
499 supplied: "mp3", // Defines which formats jPlayer will try and support and the priority by the order. 1st is highest,
500 auroraFormats: "wav", // List the aurora.js codecs being loaded externally. Its core supports "wav". Specify format in jPlayer context. EG., The aac.js codec gives the "m4a" format.
501 preload: 'metadata', // HTML5 Spec values: none, metadata, auto.
502 volume: 0.8, // The volume. Number 0 to 1.
504 remainingDuration: false, // When true, the remaining time is shown in the duration GUI element.
505 toggleDuration: false, // When true, clicks on the duration toggle between the duration and remaining display.
506 captureDuration: true, // When true, clicks on the duration are captured and no longer propagate up the DOM.
508 defaultPlaybackRate: 1,
509 minPlaybackRate: 0.5,
511 wmode: "opaque", // Valid wmode: window, transparent, opaque, direct, gpu.
512 backgroundColor: "#000000", // To define the jPlayer div and Flash background color.
513 cssSelectorAncestor: "#jp_container_1",
514 cssSelector: { // * denotes properties that should only be required when video media type required. _cssSelector() would require changes to enable splitting these into Audio and Video defaults.
515 videoPlay: ".jp-video-play", // *
519 seekBar: ".jp-seek-bar",
520 playBar: ".jp-play-bar",
522 unmute: ".jp-unmute",
523 volumeBar: ".jp-volume-bar",
524 volumeBarValue: ".jp-volume-bar-value",
525 volumeMax: ".jp-volume-max",
526 playbackRateBar: ".jp-playback-rate-bar",
527 playbackRateBarValue: ".jp-playback-rate-bar-value",
528 currentTime: ".jp-current-time",
529 duration: ".jp-duration",
531 fullScreen: ".jp-full-screen", // *
532 restoreScreen: ".jp-restore-screen", // *
533 repeat: ".jp-repeat",
534 repeatOff: ".jp-repeat-off",
535 gui: ".jp-gui", // The interface used with autohide feature.
536 noSolution: ".jp-no-solution" // For error feedback when jPlayer cannot find a solution.
538 stateClass: { // Classes added to the cssSelectorAncestor to indicate the state.
539 playing: "jp-state-playing",
540 seeking: "jp-state-seeking",
541 muted: "jp-state-muted",
542 looped: "jp-state-looped",
543 fullScreen: "jp-state-full-screen",
544 noVolume: "jp-state-no-volume"
546 useStateClassSkin: false, // A state class skin relies on the state classes to change the visual appearance. The single control toggles the effect, for example: play then pause, mute then unmute.
547 autoBlur: true, // GUI control handlers will drop focus after clicks.
548 smoothPlayBar: false, // Smooths the play bar transitions, which affects clicks and short media with big changes per second.
549 fullScreen: false, // Native Full Screen
552 restored: false, // Controls the interface autohide feature.
553 full: true, // Controls the interface autohide feature.
554 fadeIn: 200, // Milliseconds. The period of the fadeIn anim.
555 fadeOut: 600, // Milliseconds. The period of the fadeOut anim.
556 hold: 1000 // Milliseconds. The period of the pause before autohide beings.
559 repeat: function(event) { // The default jPlayer repeat event handler
560 if(event.jPlayer.options.loop) {
561 $(this).unbind(".jPlayerRepeat").bind($.jPlayer.event.ended + ".jPlayer.jPlayerRepeat", function() {
562 $(this).jPlayer("play");
565 $(this).unbind(".jPlayerRepeat");
568 nativeVideoControls: {
569 // Works well on standard browsers.
570 // Phone and tablet browsers can have problems with the controls disappearing.
573 msie: /msie [0-6]\./,
574 ipad: /ipad.*?os [0-4]\./,
577 android_pad: /android [0-3]\.(?!.*?mobile)/,
578 android_phone: /(?=.*android)(?!.*chrome)(?=.*mobile)/,
579 blackberry: /blackberry/,
580 windows_ce: /windows ce/,
581 iemobile: /iemobile/,
588 android_pad: /android(?!.*?mobile)/,
589 android_phone: /android.*?mobile/,
590 blackberry: /blackberry/,
591 windows_ce: /windows ce/,
592 iemobile: /iemobile/,
597 // Specific time format for this instance. The supported options are defined in $.jPlayer.timeFormat
598 // For the undefined options we use the default from $.jPlayer.timeFormat
600 keyEnabled: false, // Enables keyboard controls.
601 audioFullScreen: false, // Enables keyboard controls to enter full screen with audio media.
602 keyBindings: { // The key control object, defining the key codes and the functions to execute.
603 // The parameter, f = $.jPlayer.focus, will be checked truethy before attempting to call any of these functions.
604 // Properties may be added to this object, in key/fn pairs, to enable other key controls. EG, for the playlist add-on.
608 if(f.status.paused) {
618 if(f.status.video || f.options.audioFullScreen) {
619 f._setOption("fullScreen", !f.options.fullScreen);
626 f._muted(!f.options.muted);
632 f.volume(f.options.volume + 0.1);
638 f.volume(f.options.volume - 0.1);
644 f._loop(!f.options.loop);
648 verticalVolume: false, // Calculate volume from the bottom of the volume bar. Default is from the left. Also volume affects either width or height.
649 verticalPlaybackRate: false,
650 globalVolume: false, // Set to make volume and muted changes affect all jPlayer instances with this option enabled
651 idPrefix: "jp", // Prefix for the ids of html elements created by jPlayer. For flash, this must not include characters: . - + * / \
652 noConflict: "jQuery",
653 emulateHtml: false, // Emulates the HTML5 Media element on the jPlayer element.
654 consoleAlerts: true, // Alerts are sent to the console.log() instead of alert().
674 cssClass: "jp-video-270p"
679 cssClass: "jp-video-full"
682 instances: {}, // Static Object
683 status: { // Instanced in _init()
689 waitForPlay: true, // Same as waitForLoad except in case where preloading.
692 video: false, // True if playing a video
694 currentPercentRelative: 0,
695 currentPercentAbsolute: 0,
699 videoWidth: 0, // Intrinsic width of the video in pixels.
700 videoHeight: 0, // Intrinsic height of the video in pixels.
703 playbackRate: 1, // Warning - Now both an option and a status property
706 /* Persistant status properties created dynamically at _init():
713 playbackRateEnabled // Warning - Technically, we can have both Flash and HTML, so this might not be correct if the Flash is active. That is a niche case.
717 internal: { // Instanced in _init()
719 // instance: undefined
720 // domNode: undefined
721 // htmlDlyCmdId: undefined
722 // autohideId: undefined
726 solution: { // Static Object: Defines the solutions built in jPlayer.
731 // 'MPEG-4 support' : canPlayType('video/mp4; codecs="mp4v.20.8"')
732 format: { // Static Object
739 codec: 'audio/mp4; codecs="mp4a.40.2"',
743 m3u8a: { // AAC / MP4 / Apple HLS
744 codec: 'application/vnd.apple.mpegurl; codecs="mp4a.40.2"',
749 codec: 'audio/mpegurl',
754 codec: 'audio/ogg; codecs="vorbis, opus"',
759 codec: 'audio/x-flac',
764 codec: 'audio/wav; codecs="1"',
769 codec: 'audio/webm; codecs="vorbis"',
774 codec: 'audio/x-flv',
778 rtmpa: { // RTMP AUDIO
779 codec: 'audio/rtmp; codecs="rtmp"',
783 m4v: { // H.264 / MP4
784 codec: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"',
788 m3u8v: { // H.264 / AAC / MP4 / Apple HLS
789 codec: 'application/vnd.apple.mpegurl; codecs="avc1.42E01E, mp4a.40.2"',
794 codec: 'audio/mpegurl',
799 codec: 'video/ogg; codecs="theora, vorbis"',
804 codec: 'video/webm; codecs="vorbis, vp8"',
809 codec: 'video/x-flv',
813 rtmpv: { // RTMP VIDEO
814 codec: 'video/rtmp; codecs="rtmp"',
822 this.element.empty();
824 this.status = $.extend({}, this.status); // Copy static to unique instance.
825 this.internal = $.extend({}, this.internal); // Copy static to unique instance.
827 // Initialize the time format
828 this.options.timeFormat = $.extend({}, $.jPlayer.timeFormat, this.options.timeFormat);
830 // On iOS, assume commands will be ignored before user initiates them.
831 this.internal.cmdsIgnored = $.jPlayer.platform.ipad || $.jPlayer.platform.iphone || $.jPlayer.platform.ipod;
833 this.internal.domNode = this.element.get(0);
835 // Add key bindings focus to 1st jPlayer instanced with key control enabled.
836 if(this.options.keyEnabled && !$.jPlayer.focus) {
837 $.jPlayer.focus = this;
840 // A fix for Android where older (2.3) and even some 4.x devices fail to work when changing the *audio* SRC and then playing immediately.
842 setMedia: false, // True when media set
843 play: false, // True when a progress event will instruct the media to play
844 pause: false, // True when a progress event will instruct the media to pause at a time.
845 time: NaN // The play(time) parameter
847 if($.jPlayer.platform.android) {
848 this.options.preload = this.options.preload !== 'auto' ? 'metadata' : 'auto'; // Default to metadata, but allow auto.
851 this.formats = []; // Array based on supplied string option. Order defines priority.
852 this.solutions = []; // Array based on solution string option. Order defines priority.
853 this.require = {}; // Which media types are required: video, audio.
855 this.htmlElement = {}; // DOM elements created by jPlayer
856 this.html = {}; // In _init()'s this.desired code and setmedia(): Accessed via this[solution], where solution from this.solutions array.
857 this.html.audio = {};
858 this.html.video = {};
859 this.aurora = {}; // In _init()'s this.desired code and setmedia(): Accessed via this[solution], where solution from this.solutions array.
860 this.aurora.formats = [];
861 this.aurora.properties = [];
862 this.flash = {}; // In _init()'s this.desired code and setmedia(): Accessed via this[solution], where solution from this.solutions array.
865 this.css.cs = {}; // Holds the css selector strings
866 this.css.jq = {}; // Holds jQuery selectors. ie., $(css.cs.method)
868 this.ancestorJq = []; // Holds jQuery selector of cssSelectorAncestor. Init would use $() instead of [], but it is only 1.4+
870 this.options.volume = this._limitValue(this.options.volume, 0, 1); // Limit volume value's bounds.
872 // Create the formats array, with prority based on the order of the supplied formats string
873 $.each(this.options.supplied.toLowerCase().split(","), function(index1, value1) {
874 var format = value1.replace(/^\s+|\s+$/g, ""); //trim
875 if(self.format[format]) { // Check format is valid.
876 var dupFound = false;
877 $.each(self.formats, function(index2, value2) { // Check for duplicates
878 if(format === value2) {
884 self.formats.push(format);
889 // Create the solutions array, with prority based on the order of the solution string
890 $.each(this.options.solution.toLowerCase().split(","), function(index1, value1) {
891 var solution = value1.replace(/^\s+|\s+$/g, ""); //trim
892 if(self.solution[solution]) { // Check solution is valid.
893 var dupFound = false;
894 $.each(self.solutions, function(index2, value2) { // Check for duplicates
895 if(solution === value2) {
901 self.solutions.push(solution);
906 // Create Aurora.js formats array
907 $.each(this.options.auroraFormats.toLowerCase().split(","), function(index1, value1) {
908 var format = value1.replace(/^\s+|\s+$/g, ""); //trim
909 if(self.format[format]) { // Check format is valid.
910 var dupFound = false;
911 $.each(self.aurora.formats, function(index2, value2) { // Check for duplicates
912 if(format === value2) {
918 self.aurora.formats.push(format);
923 this.internal.instance = "jp_" + this.count;
924 this.instances[this.internal.instance] = this.element;
926 // Check the jPlayer div has an id and create one if required. Important for Flash to know the unique id for comms.
927 if(!this.element.attr("id")) {
928 this.element.attr("id", this.options.idPrefix + "_jplayer_" + this.count);
931 this.internal.self = $.extend({}, {
932 id: this.element.attr("id"),
935 this.internal.audio = $.extend({}, {
936 id: this.options.idPrefix + "_audio_" + this.count,
939 this.internal.video = $.extend({}, {
940 id: this.options.idPrefix + "_video_" + this.count,
943 this.internal.flash = $.extend({}, {
944 id: this.options.idPrefix + "_flash_" + this.count,
946 swf: this.options.swfPath + (this.options.swfPath.toLowerCase().slice(-4) !== ".swf" ? (this.options.swfPath && this.options.swfPath.slice(-1) !== "/" ? "/" : "") + "jquery.jplayer.swf" : "")
948 this.internal.poster = $.extend({}, {
949 id: this.options.idPrefix + "_poster_" + this.count,
953 // Register listeners defined in the constructor
954 $.each($.jPlayer.event, function(eventName,eventType) {
955 if(self.options[eventName] !== undefined) {
956 self.element.bind(eventType + ".jPlayer", self.options[eventName]); // With .jPlayer namespace.
957 self.options[eventName] = undefined; // Destroy the handler pointer copy on the options. Reason, events can be added/removed in other ways so this could be obsolete and misleading.
961 // Determine if we require solutions for audio, video or both media types.
962 this.require.audio = false;
963 this.require.video = false;
964 $.each(this.formats, function(priority, format) {
965 self.require[self.format[format].media] = true;
968 // Now required types are known, finish the options default settings.
969 if(this.require.video) {
970 this.options = $.extend(true, {},
975 this.options = $.extend(true, {},
980 this._setSize(); // update status and jPlayer element size
982 // Determine the status for Blocklisted options.
983 this.status.nativeVideoControls = this._uaBlocklist(this.options.nativeVideoControls);
984 this.status.noFullWindow = this._uaBlocklist(this.options.noFullWindow);
985 this.status.noVolume = this._uaBlocklist(this.options.noVolume);
987 // Create event handlers if native fullscreen is supported
988 if($.jPlayer.nativeFeatures.fullscreen.api.fullscreenEnabled) {
989 this._fullscreenAddEventListeners();
992 // The native controls are only for video and are disabled when audio is also used.
993 this._restrictNativeVideoControls();
995 // Create the poster image.
996 this.htmlElement.poster = document.createElement('img');
997 this.htmlElement.poster.id = this.internal.poster.id;
998 this.htmlElement.poster.onload = function() { // Note that this did not work on Firefox 3.6: poster.addEventListener("onload", function() {}, false); Did not investigate x-browser.
999 if(!self.status.video || self.status.waitForPlay) {
1000 self.internal.poster.jq.show();
1003 this.element.append(this.htmlElement.poster);
1004 this.internal.poster.jq = $("#" + this.internal.poster.id);
1005 this.internal.poster.jq.css({'width': this.status.width, 'height': this.status.height});
1006 this.internal.poster.jq.hide();
1007 this.internal.poster.jq.bind("click.jPlayer", function() {
1008 self._trigger($.jPlayer.event.click);
1011 // Generate the required media elements
1012 this.html.audio.available = false;
1013 if(this.require.audio) { // If a supplied format is audio
1014 this.htmlElement.audio = document.createElement('audio');
1015 this.htmlElement.audio.id = this.internal.audio.id;
1016 this.html.audio.available = !!this.htmlElement.audio.canPlayType && this._testCanPlayType(this.htmlElement.audio); // Test is for IE9 on Win Server 2008.
1018 this.html.video.available = false;
1019 if(this.require.video) { // If a supplied format is video
1020 this.htmlElement.video = document.createElement('video');
1021 this.htmlElement.video.id = this.internal.video.id;
1022 this.html.video.available = !!this.htmlElement.video.canPlayType && this._testCanPlayType(this.htmlElement.video); // Test is for IE9 on Win Server 2008.
1025 this.flash.available = this._checkForFlash(10.1);
1027 this.html.canPlay = {};
1028 this.aurora.canPlay = {};
1029 this.flash.canPlay = {};
1030 $.each(this.formats, function(priority, format) {
1031 self.html.canPlay[format] = self.html[self.format[format].media].available && "" !== self.htmlElement[self.format[format].media].canPlayType(self.format[format].codec);
1032 self.aurora.canPlay[format] = ($.inArray(format, self.aurora.formats) > -1);
1033 self.flash.canPlay[format] = self.format[format].flashCanPlay && self.flash.available;
1035 this.html.desired = false;
1036 this.aurora.desired = false;
1037 this.flash.desired = false;
1038 $.each(this.solutions, function(solutionPriority, solution) {
1039 if(solutionPriority === 0) {
1040 self[solution].desired = true;
1042 var audioCanPlay = false;
1043 var videoCanPlay = false;
1044 $.each(self.formats, function(formatPriority, format) {
1045 if(self[self.solutions[0]].canPlay[format]) { // The other solution can play
1046 if(self.format[format].media === 'video') {
1047 videoCanPlay = true;
1049 audioCanPlay = true;
1053 self[solution].desired = (self.require.audio && !audioCanPlay) || (self.require.video && !videoCanPlay);
1056 // This is what jPlayer will support, based on solution and supplied.
1057 this.html.support = {};
1058 this.aurora.support = {};
1059 this.flash.support = {};
1060 $.each(this.formats, function(priority, format) {
1061 self.html.support[format] = self.html.canPlay[format] && self.html.desired;
1062 self.aurora.support[format] = self.aurora.canPlay[format] && self.aurora.desired;
1063 self.flash.support[format] = self.flash.canPlay[format] && self.flash.desired;
1065 // If jPlayer is supporting any format in a solution, then the solution is used.
1066 this.html.used = false;
1067 this.aurora.used = false;
1068 this.flash.used = false;
1069 $.each(this.solutions, function(solutionPriority, solution) {
1070 $.each(self.formats, function(formatPriority, format) {
1071 if(self[solution].support[format]) {
1072 self[solution].used = true;
1078 // Init solution active state and the event gates to false.
1079 this._resetActive();
1082 // Set up the css selectors for the control and feedback entities.
1083 this._cssSelectorAncestor(this.options.cssSelectorAncestor);
1085 // If neither html nor aurora nor flash are being used by this browser, then media playback is not possible. Trigger an error event.
1086 if(!(this.html.used || this.aurora.used || this.flash.used)) {
1088 type: $.jPlayer.error.NO_SOLUTION,
1089 context: "{solution:'" + this.options.solution + "', supplied:'" + this.options.supplied + "'}",
1090 message: $.jPlayer.errorMsg.NO_SOLUTION,
1091 hint: $.jPlayer.errorHint.NO_SOLUTION
1093 if(this.css.jq.noSolution.length) {
1094 this.css.jq.noSolution.show();
1097 if(this.css.jq.noSolution.length) {
1098 this.css.jq.noSolution.hide();
1102 // Add the flash solution if it is being used.
1103 if(this.flash.used) {
1105 flashVars = 'jQuery=' + encodeURI(this.options.noConflict) + '&id=' + encodeURI(this.internal.self.id) + '&vol=' + this.options.volume + '&muted=' + this.options.muted;
1107 // Code influenced by SWFObject 2.2: http://code.google.com/p/swfobject/
1108 // Non IE browsers have an initial Flash size of 1 by 1 otherwise the wmode affected the Flash ready event.
1110 if($.jPlayer.browser.msie && (Number($.jPlayer.browser.version) < 9 || $.jPlayer.browser.documentMode < 9)) {
1111 var objStr = '<object id="' + this.internal.flash.id + '" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="0" height="0" tabindex="-1"></object>';
1114 '<param name="movie" value="' + this.internal.flash.swf + '" />',
1115 '<param name="FlashVars" value="' + flashVars + '" />',
1116 '<param name="allowScriptAccess" value="always" />',
1117 '<param name="bgcolor" value="' + this.options.backgroundColor + '" />',
1118 '<param name="wmode" value="' + this.options.wmode + '" />'
1121 htmlObj = document.createElement(objStr);
1122 for(var i=0; i < paramStr.length; i++) {
1123 htmlObj.appendChild(document.createElement(paramStr[i]));
1126 var createParam = function(el, n, v) {
1127 var p = document.createElement("param");
1128 p.setAttribute("name", n);
1129 p.setAttribute("value", v);
1133 htmlObj = document.createElement("object");
1134 htmlObj.setAttribute("id", this.internal.flash.id);
1135 htmlObj.setAttribute("name", this.internal.flash.id);
1136 htmlObj.setAttribute("data", this.internal.flash.swf);
1137 htmlObj.setAttribute("type", "application/x-shockwave-flash");
1138 htmlObj.setAttribute("width", "1"); // Non-zero
1139 htmlObj.setAttribute("height", "1"); // Non-zero
1140 htmlObj.setAttribute("tabindex", "-1");
1141 createParam(htmlObj, "flashvars", flashVars);
1142 createParam(htmlObj, "allowscriptaccess", "always");
1143 createParam(htmlObj, "bgcolor", this.options.backgroundColor);
1144 createParam(htmlObj, "wmode", this.options.wmode);
1147 this.element.append(htmlObj);
1148 this.internal.flash.jq = $(htmlObj);
1151 // Setup playbackRate ability before using _addHtmlEventListeners()
1152 if(this.html.used && !this.flash.used) { // If only HTML
1153 // Using the audio element capabilities for playbackRate. ie., Assuming video element is the same.
1154 this.status.playbackRateEnabled = this._testPlaybackRate('audio');
1156 this.status.playbackRateEnabled = false;
1159 this._updatePlaybackRate();
1161 // Add the HTML solution if being used.
1162 if(this.html.used) {
1164 // The HTML Audio handlers
1165 if(this.html.audio.available) {
1166 this._addHtmlEventListeners(this.htmlElement.audio, this.html.audio);
1167 this.element.append(this.htmlElement.audio);
1168 this.internal.audio.jq = $("#" + this.internal.audio.id);
1171 // The HTML Video handlers
1172 if(this.html.video.available) {
1173 this._addHtmlEventListeners(this.htmlElement.video, this.html.video);
1174 this.element.append(this.htmlElement.video);
1175 this.internal.video.jq = $("#" + this.internal.video.id);
1176 if(this.status.nativeVideoControls) {
1177 this.internal.video.jq.css({'width': this.status.width, 'height': this.status.height});
1179 this.internal.video.jq.css({'width':'0px', 'height':'0px'}); // Using size 0x0 since a .hide() causes issues in iOS
1181 this.internal.video.jq.bind("click.jPlayer", function() {
1182 self._trigger($.jPlayer.event.click);
1187 // Add the Aurora.js solution if being used.
1188 if(this.aurora.used) {
1189 // Aurora.js player need to be created for each media, see setMedia function.
1192 // Create the bridge that emulates the HTML Media element on the jPlayer DIV
1193 if( this.options.emulateHtml ) {
1194 this._emulateHtmlBridge();
1197 if((this.html.used || this.aurora.used) && !this.flash.used) { // If only HTML, then emulate flash ready() call after 100ms.
1198 setTimeout( function() {
1199 self.internal.ready = true;
1200 self.version.flash = "n/a";
1201 self._trigger($.jPlayer.event.repeat); // Trigger the repeat event so its handler can initialize itself with the loop option.
1202 self._trigger($.jPlayer.event.ready);
1206 // Initialize the interface components with the options.
1207 this._updateNativeVideoControls();
1208 // The other controls are now setup in _cssSelectorAncestor()
1209 if(this.css.jq.videoPlay.length) {
1210 this.css.jq.videoPlay.hide();
1213 $.jPlayer.prototype.count++; // Change static variable via prototype.
1215 destroy: function() {
1216 // MJP: The background change remains. Would need to store the original to restore it correctly.
1217 // MJP: The jPlayer element's size change remains.
1219 // Clear the media to reset the GUI and stop any downloads. Streams on some browsers had persited. (Chrome)
1221 // Remove the size/sizeFull cssClass from the cssSelectorAncestor
1222 this._removeUiClass();
1223 // Remove the times from the GUI
1224 if(this.css.jq.currentTime.length) {
1225 this.css.jq.currentTime.text("");
1227 if(this.css.jq.duration.length) {
1228 this.css.jq.duration.text("");
1230 // Remove any bindings from the interface controls.
1231 $.each(this.css.jq, function(fn, jq) {
1232 // Check selector is valid before trying to execute method.
1234 jq.unbind(".jPlayer");
1237 // Remove the click handlers for $.jPlayer.event.click
1238 this.internal.poster.jq.unbind(".jPlayer");
1239 if(this.internal.video.jq) {
1240 this.internal.video.jq.unbind(".jPlayer");
1242 // Remove the fullscreen event handlers
1243 this._fullscreenRemoveEventListeners();
1244 // Remove key bindings
1245 if(this === $.jPlayer.focus) {
1246 $.jPlayer.focus = null;
1248 // Destroy the HTML bridge.
1249 if(this.options.emulateHtml) {
1250 this._destroyHtmlBridge();
1252 this.element.removeData("jPlayer"); // Remove jPlayer data
1253 this.element.unbind(".jPlayer"); // Remove all event handlers created by the jPlayer constructor
1254 this.element.empty(); // Remove the inserted child elements
1256 delete this.instances[this.internal.instance]; // Clear the instance on the static instance object
1258 destroyRemoved: function() { // Destroy any instances that have gone away.
1260 $.each(this.instances, function(i, element) {
1261 if(self.element !== element) { // Do not destroy this instance.
1262 if(!element.data("jPlayer")) { // Check that element is a real jPlayer.
1263 element.jPlayer("destroy");
1264 delete self.instances[i];
1269 enable: function() { // Plan to implement
1270 // options.disabled = false
1272 disable: function () { // Plan to implement
1273 // options.disabled = true
1275 _testCanPlayType: function(elem) {
1276 // IE9 on Win Server 2008 did not implement canPlayType(), but it has the property.
1278 elem.canPlayType(this.format.mp3.codec); // The type is irrelevant.
1284 _testPlaybackRate: function(type) {
1285 // type: String 'audio' or 'video'
1287 type = typeof type === 'string' ? type : 'audio';
1288 el = document.createElement(type);
1289 // Wrapping in a try/catch, just in case older HTML5 browsers throw and error.
1291 if('playbackRate' in el) {
1292 el.playbackRate = rate;
1293 return el.playbackRate === rate;
1301 _uaBlocklist: function(list) {
1302 // list : object with properties that are all regular expressions. Property names are irrelevant.
1303 // Returns true if the user agent is matched in list.
1304 var ua = navigator.userAgent.toLowerCase(),
1307 $.each(list, function(p, re) {
1308 if(re && re.test(ua)) {
1310 return false; // exit $.each.
1315 _restrictNativeVideoControls: function() {
1316 // Fallback to noFullWindow when nativeVideoControls is true and audio media is being used. Affects when both media types are used.
1317 if(this.require.audio) {
1318 if(this.status.nativeVideoControls) {
1319 this.status.nativeVideoControls = false;
1320 this.status.noFullWindow = true;
1324 _updateNativeVideoControls: function() {
1325 if(this.html.video.available && this.html.used) {
1326 // Turn the HTML Video controls on/off
1327 this.htmlElement.video.controls = this.status.nativeVideoControls;
1328 // Show/hide the jPlayer GUI.
1329 this._updateAutohide();
1330 // For when option changed. The poster image is not updated, as it is dealt with in setMedia(). Acceptable degradation since seriously doubt these options will change on the fly. Can again review later.
1331 if(this.status.nativeVideoControls && this.require.video) {
1332 this.internal.poster.jq.hide();
1333 this.internal.video.jq.css({'width': this.status.width, 'height': this.status.height});
1334 } else if(this.status.waitForPlay && this.status.video) {
1335 this.internal.poster.jq.show();
1336 this.internal.video.jq.css({'width': '0px', 'height': '0px'});
1340 _addHtmlEventListeners: function(mediaElement, entity) {
1342 mediaElement.preload = this.options.preload;
1343 mediaElement.muted = this.options.muted;
1344 mediaElement.volume = this.options.volume;
1346 if(this.status.playbackRateEnabled) {
1347 mediaElement.defaultPlaybackRate = this.options.defaultPlaybackRate;
1348 mediaElement.playbackRate = this.options.playbackRate;
1351 // Create the event listeners
1352 // Only want the active entity to affect jPlayer and bubble events.
1353 // Using entity.gate so that object is referenced and gate property always current
1355 mediaElement.addEventListener("progress", function() {
1357 if(self.internal.cmdsIgnored && this.readyState > 0) { // Detect iOS executed the command
1358 self.internal.cmdsIgnored = false;
1360 self._getHtmlStatus(mediaElement);
1361 self._updateInterface();
1362 self._trigger($.jPlayer.event.progress);
1365 mediaElement.addEventListener("loadeddata", function() {
1367 self.androidFix.setMedia = false; // Disable the fix after the first progress event.
1368 if(self.androidFix.play) { // Play Android audio - performing the fix.
1369 self.androidFix.play = false;
1370 self.play(self.androidFix.time);
1372 if(self.androidFix.pause) { // Pause Android audio at time - performing the fix.
1373 self.androidFix.pause = false;
1374 self.pause(self.androidFix.time);
1376 self._trigger($.jPlayer.event.loadeddata);
1379 mediaElement.addEventListener("timeupdate", function() {
1381 self._getHtmlStatus(mediaElement);
1382 self._updateInterface();
1383 self._trigger($.jPlayer.event.timeupdate);
1386 mediaElement.addEventListener("durationchange", function() {
1388 self._getHtmlStatus(mediaElement);
1389 self._updateInterface();
1390 self._trigger($.jPlayer.event.durationchange);
1393 mediaElement.addEventListener("play", function() {
1395 self._updateButtons(true);
1396 self._html_checkWaitForPlay(); // So the native controls update this variable and puts the hidden interface in the correct state. Affects toggling native controls.
1397 self._trigger($.jPlayer.event.play);
1400 mediaElement.addEventListener("playing", function() {
1402 self._updateButtons(true);
1404 self._trigger($.jPlayer.event.playing);
1407 mediaElement.addEventListener("pause", function() {
1409 self._updateButtons(false);
1410 self._trigger($.jPlayer.event.pause);
1413 mediaElement.addEventListener("waiting", function() {
1416 self._trigger($.jPlayer.event.waiting);
1419 mediaElement.addEventListener("seeking", function() {
1422 self._trigger($.jPlayer.event.seeking);
1425 mediaElement.addEventListener("seeked", function() {
1428 self._trigger($.jPlayer.event.seeked);
1431 mediaElement.addEventListener("volumechange", function() {
1433 // Read the values back from the element as the Blackberry PlayBook shares the volume with the physical buttons master volume control.
1434 // However, when tested 6th July 2011, those buttons do not generate an event. The physical play/pause button does though.
1435 self.options.volume = mediaElement.volume;
1436 self.options.muted = mediaElement.muted;
1438 self._updateVolume();
1439 self._trigger($.jPlayer.event.volumechange);
1442 mediaElement.addEventListener("ratechange", function() {
1444 self.options.defaultPlaybackRate = mediaElement.defaultPlaybackRate;
1445 self.options.playbackRate = mediaElement.playbackRate;
1446 self._updatePlaybackRate();
1447 self._trigger($.jPlayer.event.ratechange);
1450 mediaElement.addEventListener("suspend", function() { // Seems to be the only way of capturing that the iOS4 browser did not actually play the media from the page code. ie., It needs a user gesture.
1453 self._trigger($.jPlayer.event.suspend);
1456 mediaElement.addEventListener("ended", function() {
1458 // Order of the next few commands are important. Change the time and then pause.
1459 // Solves a bug in Firefox, where issuing pause 1st causes the media to play from the start. ie., The pause is ignored.
1460 if(!$.jPlayer.browser.webkit) { // Chrome crashes if you do this in conjunction with a setMedia command in an ended event handler. ie., The playlist demo.
1461 self.htmlElement.media.currentTime = 0; // Safari does not care about this command. ie., It works with or without this line. (Both Safari and Chrome are Webkit.)
1463 self.htmlElement.media.pause(); // Pause otherwise a click on the progress bar will play from that point, when it shouldn't, since it stopped playback.
1464 self._updateButtons(false);
1465 self._getHtmlStatus(mediaElement, true); // With override true. Otherwise Chrome leaves progress at full.
1466 self._updateInterface();
1467 self._trigger($.jPlayer.event.ended);
1470 mediaElement.addEventListener("error", function() {
1472 self._updateButtons(false);
1474 if(self.status.srcSet) { // Deals with case of clearMedia() causing an error event.
1475 clearTimeout(self.internal.htmlDlyCmdId); // Clears any delayed commands used in the HTML solution.
1476 self.status.waitForLoad = true; // Allows the load operation to try again.
1477 self.status.waitForPlay = true; // Reset since a play was captured.
1478 if(self.status.video && !self.status.nativeVideoControls) {
1479 self.internal.video.jq.css({'width':'0px', 'height':'0px'});
1481 if(self._validString(self.status.media.poster) && !self.status.nativeVideoControls) {
1482 self.internal.poster.jq.show();
1484 if(self.css.jq.videoPlay.length) {
1485 self.css.jq.videoPlay.show();
1488 type: $.jPlayer.error.URL,
1489 context: self.status.src, // this.src shows absolute urls. Want context to show the url given.
1490 message: $.jPlayer.errorMsg.URL,
1491 hint: $.jPlayer.errorHint.URL
1496 // Create all the other event listeners that bubble up to a jPlayer event from html, without being used by jPlayer.
1497 $.each($.jPlayer.htmlEvent, function(i, eventType) {
1498 mediaElement.addEventListener(this, function() {
1500 self._trigger($.jPlayer.event[eventType]);
1505 _addAuroraEventListeners : function(player, entity) {
1507 //player.preload = this.options.preload;
1508 //player.muted = this.options.muted;
1509 player.volume = this.options.volume * 100;
1511 // Create the event listeners
1512 // Only want the active entity to affect jPlayer and bubble events.
1513 // Using entity.gate so that object is referenced and gate property always current
1515 player.on("progress", function() {
1517 if(self.internal.cmdsIgnored && this.readyState > 0) { // Detect iOS executed the command
1518 self.internal.cmdsIgnored = false;
1520 self._getAuroraStatus(player);
1521 self._updateInterface();
1522 self._trigger($.jPlayer.event.progress);
1523 // Progress with song duration, we estimate timeupdate need to be triggered too.
1524 if (player.duration > 0) {
1525 self._trigger($.jPlayer.event.timeupdate);
1529 player.on("ready", function() {
1531 self._trigger($.jPlayer.event.loadeddata);
1534 player.on("duration", function() {
1536 self._getAuroraStatus(player);
1537 self._updateInterface();
1538 self._trigger($.jPlayer.event.durationchange);
1541 player.on("end", function() {
1543 // Order of the next few commands are important. Change the time and then pause.
1544 self._updateButtons(false);
1545 self._getAuroraStatus(player, true);
1546 self._updateInterface();
1547 self._trigger($.jPlayer.event.ended);
1550 player.on("error", function() {
1552 self._updateButtons(false);
1554 if(self.status.srcSet) { // Deals with case of clearMedia() causing an error event.
1555 self.status.waitForLoad = true; // Allows the load operation to try again.
1556 self.status.waitForPlay = true; // Reset since a play was captured.
1557 if(self.status.video && !self.status.nativeVideoControls) {
1558 self.internal.video.jq.css({'width':'0px', 'height':'0px'});
1560 if(self._validString(self.status.media.poster) && !self.status.nativeVideoControls) {
1561 self.internal.poster.jq.show();
1563 if(self.css.jq.videoPlay.length) {
1564 self.css.jq.videoPlay.show();
1567 type: $.jPlayer.error.URL,
1568 context: self.status.src, // this.src shows absolute urls. Want context to show the url given.
1569 message: $.jPlayer.errorMsg.URL,
1570 hint: $.jPlayer.errorHint.URL
1576 _getHtmlStatus: function(media, override) {
1577 var ct = 0, cpa = 0, sp = 0, cpr = 0;
1579 // Fixes the duration bug in iOS, where the durationchange event occurs when media.duration is not always correct.
1580 // Fixes the initial duration bug in BB OS7, where the media.duration is infinity and displays as NaN:NaN due to Date() using inifity.
1581 if(isFinite(media.duration)) {
1582 this.status.duration = media.duration;
1585 ct = media.currentTime;
1586 cpa = (this.status.duration > 0) ? 100 * ct / this.status.duration : 0;
1587 if((typeof media.seekable === "object") && (media.seekable.length > 0)) {
1588 sp = (this.status.duration > 0) ? 100 * media.seekable.end(media.seekable.length-1) / this.status.duration : 100;
1589 cpr = (this.status.duration > 0) ? 100 * media.currentTime / media.seekable.end(media.seekable.length-1) : 0; // Duration conditional for iOS duration bug. ie., seekable.end is a NaN in that case.
1601 this.status.seekPercent = sp;
1602 this.status.currentPercentRelative = cpr;
1603 this.status.currentPercentAbsolute = cpa;
1604 this.status.currentTime = ct;
1606 this.status.remaining = this.status.duration - this.status.currentTime;
1608 this.status.videoWidth = media.videoWidth;
1609 this.status.videoHeight = media.videoHeight;
1611 this.status.readyState = media.readyState;
1612 this.status.networkState = media.networkState;
1613 this.status.playbackRate = media.playbackRate;
1614 this.status.ended = media.ended;
1616 _getAuroraStatus: function(player, override) {
1617 var ct = 0, cpa = 0, sp = 0, cpr = 0;
1619 this.status.duration = player.duration / 1000;
1621 ct = player.currentTime / 1000;
1622 cpa = (this.status.duration > 0) ? 100 * ct / this.status.duration : 0;
1623 if(player.buffered > 0) {
1624 sp = (this.status.duration > 0) ? (player.buffered * this.status.duration) / this.status.duration : 100;
1625 cpr = (this.status.duration > 0) ? ct / (player.buffered * this.status.duration) : 0;
1637 this.status.seekPercent = sp;
1638 this.status.currentPercentRelative = cpr;
1639 this.status.currentPercentAbsolute = cpa;
1640 this.status.currentTime = ct;
1642 this.status.remaining = this.status.duration - this.status.currentTime;
1644 this.status.readyState = 4; // status.readyState;
1645 this.status.networkState = 0; // status.networkState;
1646 this.status.playbackRate = 1; // status.playbackRate;
1647 this.status.ended = false; // status.ended;
1649 _resetStatus: function() {
1650 this.status = $.extend({}, this.status, $.jPlayer.prototype.status); // Maintains the status properties that persist through a reset.
1652 _trigger: function(eventType, error, warning) { // eventType always valid as called using $.jPlayer.event.eventType
1653 var event = $.Event(eventType);
1655 event.jPlayer.version = $.extend({}, this.version);
1656 event.jPlayer.options = $.extend(true, {}, this.options); // Deep copy
1657 event.jPlayer.status = $.extend(true, {}, this.status); // Deep copy
1658 event.jPlayer.html = $.extend(true, {}, this.html); // Deep copy
1659 event.jPlayer.aurora = $.extend(true, {}, this.aurora); // Deep copy
1660 event.jPlayer.flash = $.extend(true, {}, this.flash); // Deep copy
1662 event.jPlayer.error = $.extend({}, error);
1665 event.jPlayer.warning = $.extend({}, warning);
1667 this.element.trigger(event);
1669 jPlayerFlashEvent: function(eventType, status) { // Called from Flash
1670 if(eventType === $.jPlayer.event.ready) {
1671 if(!this.internal.ready) {
1672 this.internal.ready = true;
1673 this.internal.flash.jq.css({'width':'0px', 'height':'0px'}); // Once Flash generates the ready event, minimise to zero as it is not affected by wmode anymore.
1675 this.version.flash = status.version;
1676 if(this.version.needFlash !== this.version.flash) {
1678 type: $.jPlayer.error.VERSION,
1679 context: this.version.flash,
1680 message: $.jPlayer.errorMsg.VERSION + this.version.flash,
1681 hint: $.jPlayer.errorHint.VERSION
1684 this._trigger($.jPlayer.event.repeat); // Trigger the repeat event so its handler can initialize itself with the loop option.
1685 this._trigger(eventType);
1687 // This condition occurs if the Flash is hidden and then shown again.
1688 // Firefox also reloads the Flash if the CSS position changes. position:fixed is used for full screen.
1690 // Only do this if the Flash is the solution being used at the moment. Affects Media players where both solution may be being used.
1691 if(this.flash.gate) {
1693 // Send the current status to the Flash now that it is ready (available) again.
1694 if(this.status.srcSet) {
1696 // Need to read original status before issuing the setMedia command.
1697 var currentTime = this.status.currentTime,
1698 paused = this.status.paused;
1700 this.setMedia(this.status.media);
1701 this.volumeWorker(this.options.volume);
1702 if(currentTime > 0) {
1704 this.pause(currentTime);
1706 this.play(currentTime);
1710 this._trigger($.jPlayer.event.flashreset);
1714 if(this.flash.gate) {
1716 case $.jPlayer.event.progress:
1717 this._getFlashStatus(status);
1718 this._updateInterface();
1719 this._trigger(eventType);
1721 case $.jPlayer.event.timeupdate:
1722 this._getFlashStatus(status);
1723 this._updateInterface();
1724 this._trigger(eventType);
1726 case $.jPlayer.event.play:
1728 this._updateButtons(true);
1729 this._trigger(eventType);
1731 case $.jPlayer.event.pause:
1732 this._updateButtons(false);
1733 this._trigger(eventType);
1735 case $.jPlayer.event.ended:
1736 this._updateButtons(false);
1737 this._trigger(eventType);
1739 case $.jPlayer.event.click:
1740 this._trigger(eventType); // This could be dealt with by the default
1742 case $.jPlayer.event.error:
1743 this.status.waitForLoad = true; // Allows the load operation to try again.
1744 this.status.waitForPlay = true; // Reset since a play was captured.
1745 if(this.status.video) {
1746 this.internal.flash.jq.css({'width':'0px', 'height':'0px'});
1748 if(this._validString(this.status.media.poster)) {
1749 this.internal.poster.jq.show();
1751 if(this.css.jq.videoPlay.length && this.status.video) {
1752 this.css.jq.videoPlay.show();
1754 if(this.status.video) { // Set up for another try. Execute before error event.
1755 this._flash_setVideo(this.status.media);
1757 this._flash_setAudio(this.status.media);
1759 this._updateButtons(false);
1761 type: $.jPlayer.error.URL,
1763 message: $.jPlayer.errorMsg.URL,
1764 hint: $.jPlayer.errorHint.URL
1767 case $.jPlayer.event.seeking:
1769 this._trigger(eventType);
1771 case $.jPlayer.event.seeked:
1773 this._trigger(eventType);
1775 case $.jPlayer.event.ready:
1776 // The ready event is handled outside the switch statement.
1777 // Captured here otherwise 2 ready events would be generated if the ready event handler used setMedia.
1780 this._trigger(eventType);
1785 _getFlashStatus: function(status) {
1786 this.status.seekPercent = status.seekPercent;
1787 this.status.currentPercentRelative = status.currentPercentRelative;
1788 this.status.currentPercentAbsolute = status.currentPercentAbsolute;
1789 this.status.currentTime = status.currentTime;
1790 this.status.duration = status.duration;
1791 this.status.remaining = status.duration - status.currentTime;
1793 this.status.videoWidth = status.videoWidth;
1794 this.status.videoHeight = status.videoHeight;
1796 // The Flash does not generate this information in this release
1797 this.status.readyState = 4; // status.readyState;
1798 this.status.networkState = 0; // status.networkState;
1799 this.status.playbackRate = 1; // status.playbackRate;
1800 this.status.ended = false; // status.ended;
1802 _updateButtons: function(playing) {
1803 if(playing === undefined) {
1804 playing = !this.status.paused;
1806 this.status.paused = !playing;
1808 // Apply the state classes. (For the useStateClassSkin:true option)
1810 this.addStateClass('playing');
1812 this.removeStateClass('playing');
1814 if(!this.status.noFullWindow && this.options.fullWindow) {
1815 this.addStateClass('fullScreen');
1817 this.removeStateClass('fullScreen');
1819 if(this.options.loop) {
1820 this.addStateClass('looped');
1822 this.removeStateClass('looped');
1824 // Toggle the GUI element pairs. (For the useStateClassSkin:false option)
1825 if(this.css.jq.play.length && this.css.jq.pause.length) {
1827 this.css.jq.play.hide();
1828 this.css.jq.pause.show();
1830 this.css.jq.play.show();
1831 this.css.jq.pause.hide();
1834 if(this.css.jq.restoreScreen.length && this.css.jq.fullScreen.length) {
1835 if(this.status.noFullWindow) {
1836 this.css.jq.fullScreen.hide();
1837 this.css.jq.restoreScreen.hide();
1838 } else if(this.options.fullWindow) {
1839 this.css.jq.fullScreen.hide();
1840 this.css.jq.restoreScreen.show();
1842 this.css.jq.fullScreen.show();
1843 this.css.jq.restoreScreen.hide();
1846 if(this.css.jq.repeat.length && this.css.jq.repeatOff.length) {
1847 if(this.options.loop) {
1848 this.css.jq.repeat.hide();
1849 this.css.jq.repeatOff.show();
1851 this.css.jq.repeat.show();
1852 this.css.jq.repeatOff.hide();
1856 _updateInterface: function() {
1857 if(this.css.jq.seekBar.length) {
1858 this.css.jq.seekBar.width(this.status.seekPercent+"%");
1860 if(this.css.jq.playBar.length) {
1861 if(this.options.smoothPlayBar) {
1862 this.css.jq.playBar.stop().animate({
1863 width: this.status.currentPercentAbsolute+"%"
1866 this.css.jq.playBar.width(this.status.currentPercentRelative+"%");
1869 var currentTimeText = '';
1870 if(this.css.jq.currentTime.length) {
1871 currentTimeText = this._convertTime(this.status.currentTime);
1872 if(currentTimeText !== this.css.jq.currentTime.text()) {
1873 this.css.jq.currentTime.text(this._convertTime(this.status.currentTime));
1876 var durationText = '',
1877 duration = this.status.duration,
1878 remaining = this.status.remaining;
1879 if(this.css.jq.duration.length) {
1880 if(typeof this.status.media.duration === 'string') {
1881 durationText = this.status.media.duration;
1883 if(typeof this.status.media.duration === 'number') {
1884 duration = this.status.media.duration;
1885 remaining = duration - this.status.currentTime;
1887 if(this.options.remainingDuration) {
1888 durationText = (remaining > 0 ? '-' : '') + this._convertTime(remaining);
1890 durationText = this._convertTime(duration);
1893 if(durationText !== this.css.jq.duration.text()) {
1894 this.css.jq.duration.text(durationText);
1898 _convertTime: ConvertTime.prototype.time,
1899 _seeking: function() {
1900 if(this.css.jq.seekBar.length) {
1901 this.css.jq.seekBar.addClass("jp-seeking-bg");
1903 this.addStateClass('seeking');
1905 _seeked: function() {
1906 if(this.css.jq.seekBar.length) {
1907 this.css.jq.seekBar.removeClass("jp-seeking-bg");
1909 this.removeStateClass('seeking');
1911 _resetGate: function() {
1912 this.html.audio.gate = false;
1913 this.html.video.gate = false;
1914 this.aurora.gate = false;
1915 this.flash.gate = false;
1917 _resetActive: function() {
1918 this.html.active = false;
1919 this.aurora.active = false;
1920 this.flash.active = false;
1922 _escapeHtml: function(s) {
1923 return s.split('&').join('&').split('<').join('<').split('>').join('>').split('"').join('"');
1925 _qualifyURL: function(url) {
1926 var el = document.createElement('div');
1927 el.innerHTML= '<a href="' + this._escapeHtml(url) + '">x</a>';
1928 return el.firstChild.href;
1930 _absoluteMediaUrls: function(media) {
1932 $.each(media, function(type, url) {
1933 if(url && self.format[type] && url.substr(0, 5) !== "data:") {
1934 media[type] = self._qualifyURL(url);
1939 addStateClass: function(state) {
1940 if(this.ancestorJq.length) {
1941 this.ancestorJq.addClass(this.options.stateClass[state]);
1944 removeStateClass: function(state) {
1945 if(this.ancestorJq.length) {
1946 this.ancestorJq.removeClass(this.options.stateClass[state]);
1949 setMedia: function(media) {
1951 /* media[format] = String: URL of format. Must contain all of the supplied option's video or audio formats.
1952 * media.poster = String: Video poster URL.
1953 * media.track = Array: Of objects defining the track element: kind, src, srclang, label, def.
1954 * media.stream = Boolean: * NOT IMPLEMENTED * Designating actual media streams. ie., "false/undefined" for files. Plan to refresh the flash every so often.
1959 posterChanged = this.status.media.poster !== media.poster; // Compare before reset. Important for OSX Safari as this.htmlElement.poster.src is absolute, even if original poster URL was relative.
1963 this._resetActive();
1965 // Clear the Android Fix.
1966 this.androidFix.setMedia = false;
1967 this.androidFix.play = false;
1968 this.androidFix.pause = false;
1970 // Convert all media URLs to absolute URLs.
1971 media = this._absoluteMediaUrls(media);
1973 $.each(this.formats, function(formatPriority, format) {
1974 var isVideo = self.format[format].media === 'video';
1975 $.each(self.solutions, function(solutionPriority, solution) {
1976 if(self[solution].support[format] && self._validString(media[format])) { // Format supported in solution and url given for format.
1977 var isHtml = solution === 'html';
1978 var isAurora = solution === 'aurora';
1982 self.html.video.gate = true;
1983 self._html_setVideo(media);
1984 self.html.active = true;
1986 self.flash.gate = true;
1987 self._flash_setVideo(media);
1988 self.flash.active = true;
1990 if(self.css.jq.videoPlay.length) {
1991 self.css.jq.videoPlay.show();
1993 self.status.video = true;
1996 self.html.audio.gate = true;
1997 self._html_setAudio(media);
1998 self.html.active = true;
2000 // Setup the Android Fix - Only for HTML audio.
2001 if($.jPlayer.platform.android) {
2002 self.androidFix.setMedia = true;
2004 } else if(isAurora) {
2005 self.aurora.gate = true;
2006 self._aurora_setAudio(media);
2007 self.aurora.active = true;
2009 self.flash.gate = true;
2010 self._flash_setAudio(media);
2011 self.flash.active = true;
2013 if(self.css.jq.videoPlay.length) {
2014 self.css.jq.videoPlay.hide();
2016 self.status.video = false;
2020 return false; // Exit $.each
2024 return false; // Exit $.each
2029 if(!(this.status.nativeVideoControls && this.html.video.gate)) {
2030 // Set poster IMG if native video controls are not being used
2031 // Note: With IE the IMG onload event occurs immediately when cached.
2032 // Note: Poster hidden by default in _resetMedia()
2033 if(this._validString(media.poster)) {
2034 if(posterChanged) { // Since some browsers do not generate img onload event.
2035 this.htmlElement.poster.src = media.poster;
2037 this.internal.poster.jq.show();
2041 if(typeof media.title === 'string') {
2042 if(this.css.jq.title.length) {
2043 this.css.jq.title.html(media.title);
2045 if(this.htmlElement.audio) {
2046 this.htmlElement.audio.setAttribute('title', media.title);
2048 if(this.htmlElement.video) {
2049 this.htmlElement.video.setAttribute('title', media.title);
2052 this.status.srcSet = true;
2053 this.status.media = $.extend({}, media);
2054 this._updateButtons(false);
2055 this._updateInterface();
2056 this._trigger($.jPlayer.event.setmedia);
2057 } else { // jPlayer cannot support any formats provided in this browser
2058 // Send an error event
2060 type: $.jPlayer.error.NO_SUPPORT,
2061 context: "{supplied:'" + this.options.supplied + "'}",
2062 message: $.jPlayer.errorMsg.NO_SUPPORT,
2063 hint: $.jPlayer.errorHint.NO_SUPPORT
2067 _resetMedia: function() {
2068 this._resetStatus();
2069 this._updateButtons(false);
2070 this._updateInterface();
2072 this.internal.poster.jq.hide();
2074 clearTimeout(this.internal.htmlDlyCmdId);
2076 if(this.html.active) {
2077 this._html_resetMedia();
2078 } else if(this.aurora.active) {
2079 this._aurora_resetMedia();
2080 } else if(this.flash.active) {
2081 this._flash_resetMedia();
2084 clearMedia: function() {
2087 if(this.html.active) {
2088 this._html_clearMedia();
2089 } else if(this.aurora.active) {
2090 this._aurora_clearMedia();
2091 } else if(this.flash.active) {
2092 this._flash_clearMedia();
2096 this._resetActive();
2099 if(this.status.srcSet) {
2100 if(this.html.active) {
2102 } else if(this.aurora.active) {
2103 this._aurora_load();
2104 } else if(this.flash.active) {
2108 this._urlNotSetError("load");
2112 if(this.options.keyEnabled) {
2113 $.jPlayer.focus = this;
2116 play: function(time) {
2117 var guiAction = typeof time === "object"; // Flags GUI click events so we know this was not a direct command, but an action taken by the user on the GUI.
2118 if(guiAction && this.options.useStateClassSkin && !this.status.paused) {
2119 this.pause(time); // The time would be the click event, but passing it over so info is not lost.
2121 time = (typeof time === "number") ? time : NaN; // Remove jQuery event from click handler
2122 if(this.status.srcSet) {
2124 if(this.html.active) {
2125 this._html_play(time);
2126 } else if(this.aurora.active) {
2127 this._aurora_play(time);
2128 } else if(this.flash.active) {
2129 this._flash_play(time);
2132 this._urlNotSetError("play");
2136 videoPlay: function() { // Handles clicks on the play button over the video poster
2139 pause: function(time) {
2140 time = (typeof time === "number") ? time : NaN; // Remove jQuery event from click handler
2141 if(this.status.srcSet) {
2142 if(this.html.active) {
2143 this._html_pause(time);
2144 } else if(this.aurora.active) {
2145 this._aurora_pause(time);
2146 } else if(this.flash.active) {
2147 this._flash_pause(time);
2150 this._urlNotSetError("pause");
2153 tellOthers: function(command, conditions) {
2155 hasConditions = typeof conditions === 'function',
2156 args = Array.prototype.slice.call(arguments); // Convert arguments to an Array.
2158 if(typeof command !== 'string') { // Ignore, since no command.
2159 return; // Return undefined to maintain chaining.
2162 args.splice(1, 1); // Remove the conditions from the arguments
2165 $.jPlayer.prototype.destroyRemoved();
2166 $.each(this.instances, function() {
2167 // Remember that "this" is the instance's "element" in the $.each() loop.
2168 if(self.element !== this) { // Do not tell my instance.
2169 if(!hasConditions || conditions.call(this.data("jPlayer"), self)) {
2170 this.jPlayer.apply(this, args);
2175 pauseOthers: function(time) {
2176 this.tellOthers("pause", function() {
2177 // In the conditions function, the "this" context is the other instance's jPlayer object.
2178 return this.status.srcSet;
2182 if(this.status.srcSet) {
2183 if(this.html.active) {
2184 this._html_pause(0);
2185 } else if(this.aurora.active) {
2186 this._aurora_pause(0);
2187 } else if(this.flash.active) {
2188 this._flash_pause(0);
2191 this._urlNotSetError("stop");
2194 playHead: function(p) {
2195 p = this._limitValue(p, 0, 100);
2196 if(this.status.srcSet) {
2197 if(this.html.active) {
2198 this._html_playHead(p);
2199 } else if(this.aurora.active) {
2200 this._aurora_playHead(p);
2201 } else if(this.flash.active) {
2202 this._flash_playHead(p);
2205 this._urlNotSetError("playHead");
2208 _muted: function(muted) {
2209 this.mutedWorker(muted);
2210 if(this.options.globalVolume) {
2211 this.tellOthers("mutedWorker", function() {
2212 // Check the other instance has global volume enabled.
2213 return this.options.globalVolume;
2217 mutedWorker: function(muted) {
2218 this.options.muted = muted;
2219 if(this.html.used) {
2220 this._html_setProperty('muted', muted);
2222 if(this.aurora.used) {
2223 this._aurora_mute(muted);
2225 if(this.flash.used) {
2226 this._flash_mute(muted);
2229 // The HTML solution generates this event from the media element itself.
2230 if(!this.html.video.gate && !this.html.audio.gate) {
2231 this._updateMute(muted);
2232 this._updateVolume(this.options.volume);
2233 this._trigger($.jPlayer.event.volumechange);
2236 mute: function(mute) { // mute is either: undefined (true), an event object (true) or a boolean (muted).
2237 var guiAction = typeof mute === "object"; // Flags GUI click events so we know this was not a direct command, but an action taken by the user on the GUI.
2238 if(guiAction && this.options.useStateClassSkin && this.options.muted) {
2241 mute = mute === undefined ? true : !!mute;
2245 unmute: function(unmute) { // unmute is either: undefined (true), an event object (true) or a boolean (!muted).
2246 unmute = unmute === undefined ? true : !!unmute;
2247 this._muted(!unmute);
2249 _updateMute: function(mute) {
2250 if(mute === undefined) {
2251 mute = this.options.muted;
2254 this.addStateClass('muted');
2256 this.removeStateClass('muted');
2258 if(this.css.jq.mute.length && this.css.jq.unmute.length) {
2259 if(this.status.noVolume) {
2260 this.css.jq.mute.hide();
2261 this.css.jq.unmute.hide();
2263 this.css.jq.mute.hide();
2264 this.css.jq.unmute.show();
2266 this.css.jq.mute.show();
2267 this.css.jq.unmute.hide();
2271 volume: function(v) {
2272 this.volumeWorker(v);
2273 if(this.options.globalVolume) {
2274 this.tellOthers("volumeWorker", function() {
2275 // Check the other instance has global volume enabled.
2276 return this.options.globalVolume;
2280 volumeWorker: function(v) {
2281 v = this._limitValue(v, 0, 1);
2282 this.options.volume = v;
2284 if(this.html.used) {
2285 this._html_setProperty('volume', v);
2287 if(this.aurora.used) {
2288 this._aurora_volume(v);
2290 if(this.flash.used) {
2291 this._flash_volume(v);
2294 // The HTML solution generates this event from the media element itself.
2295 if(!this.html.video.gate && !this.html.audio.gate) {
2296 this._updateVolume(v);
2297 this._trigger($.jPlayer.event.volumechange);
2300 volumeBar: function(e) { // Handles clicks on the volumeBar
2301 if(this.css.jq.volumeBar.length) {
2302 // Using $(e.currentTarget) to enable multiple volume bars
2303 var $bar = $(e.currentTarget),
2304 offset = $bar.offset(),
2305 x = e.pageX - offset.left,
2307 y = $bar.height() - e.pageY + offset.top,
2309 if(this.options.verticalVolume) {
2315 if(this.options.muted) {
2319 _updateVolume: function(v) {
2320 if(v === undefined) {
2321 v = this.options.volume;
2323 v = this.options.muted ? 0 : v;
2325 if(this.status.noVolume) {
2326 this.addStateClass('noVolume');
2327 if(this.css.jq.volumeBar.length) {
2328 this.css.jq.volumeBar.hide();
2330 if(this.css.jq.volumeBarValue.length) {
2331 this.css.jq.volumeBarValue.hide();
2333 if(this.css.jq.volumeMax.length) {
2334 this.css.jq.volumeMax.hide();
2337 this.removeStateClass('noVolume');
2338 if(this.css.jq.volumeBar.length) {
2339 this.css.jq.volumeBar.show();
2341 if(this.css.jq.volumeBarValue.length) {
2342 this.css.jq.volumeBarValue.show();
2343 this.css.jq.volumeBarValue[this.options.verticalVolume ? "height" : "width"]((v*100)+"%");
2345 if(this.css.jq.volumeMax.length) {
2346 this.css.jq.volumeMax.show();
2350 volumeMax: function() { // Handles clicks on the volume max
2352 if(this.options.muted) {
2356 _cssSelectorAncestor: function(ancestor) {
2358 this.options.cssSelectorAncestor = ancestor;
2359 this._removeUiClass();
2360 this.ancestorJq = ancestor ? $(ancestor) : []; // Would use $() instead of [], but it is only 1.4+
2361 if(ancestor && this.ancestorJq.length !== 1) { // So empty strings do not generate the warning.
2363 type: $.jPlayer.warning.CSS_SELECTOR_COUNT,
2365 message: $.jPlayer.warningMsg.CSS_SELECTOR_COUNT + this.ancestorJq.length + " found for cssSelectorAncestor.",
2366 hint: $.jPlayer.warningHint.CSS_SELECTOR_COUNT
2370 $.each(this.options.cssSelector, function(fn, cssSel) {
2371 self._cssSelector(fn, cssSel);
2374 // Set the GUI to the current state.
2375 this._updateInterface();
2376 this._updateButtons();
2377 this._updateAutohide();
2378 this._updateVolume();
2381 _cssSelector: function(fn, cssSel) {
2383 if(typeof cssSel === 'string') {
2384 if($.jPlayer.prototype.options.cssSelector[fn]) {
2385 if(this.css.jq[fn] && this.css.jq[fn].length) {
2386 this.css.jq[fn].unbind(".jPlayer");
2388 this.options.cssSelector[fn] = cssSel;
2389 this.css.cs[fn] = this.options.cssSelectorAncestor + " " + cssSel;
2391 if(cssSel) { // Checks for empty string
2392 this.css.jq[fn] = $(this.css.cs[fn]);
2394 this.css.jq[fn] = []; // To comply with the css.jq[fn].length check before its use. As of jQuery 1.4 could have used $() for an empty set.
2397 if(this.css.jq[fn].length && this[fn]) {
2398 var handler = function(e) {
2401 if(self.options.autoBlur) {
2404 $(this).focus(); // Force focus for ARIA.
2407 this.css.jq[fn].bind("click.jPlayer", handler); // Using jPlayer namespace
2410 if(cssSel && this.css.jq[fn].length !== 1) { // So empty strings do not generate the warning. ie., they just remove the old one.
2412 type: $.jPlayer.warning.CSS_SELECTOR_COUNT,
2413 context: this.css.cs[fn],
2414 message: $.jPlayer.warningMsg.CSS_SELECTOR_COUNT + this.css.jq[fn].length + " found for " + fn + " method.",
2415 hint: $.jPlayer.warningHint.CSS_SELECTOR_COUNT
2420 type: $.jPlayer.warning.CSS_SELECTOR_METHOD,
2422 message: $.jPlayer.warningMsg.CSS_SELECTOR_METHOD,
2423 hint: $.jPlayer.warningHint.CSS_SELECTOR_METHOD
2428 type: $.jPlayer.warning.CSS_SELECTOR_STRING,
2430 message: $.jPlayer.warningMsg.CSS_SELECTOR_STRING,
2431 hint: $.jPlayer.warningHint.CSS_SELECTOR_STRING
2435 duration: function(e) {
2436 if(this.options.toggleDuration) {
2437 if(this.options.captureDuration) {
2438 e.stopPropagation();
2440 this._setOption("remainingDuration", !this.options.remainingDuration);
2443 seekBar: function(e) { // Handles clicks on the seekBar
2444 if(this.css.jq.seekBar.length) {
2445 // Using $(e.currentTarget) to enable multiple seek bars
2446 var $bar = $(e.currentTarget),
2447 offset = $bar.offset(),
2448 x = e.pageX - offset.left,
2454 playbackRate: function(pbr) {
2455 this._setOption("playbackRate", pbr);
2457 playbackRateBar: function(e) { // Handles clicks on the playbackRateBar
2458 if(this.css.jq.playbackRateBar.length) {
2459 // Using $(e.currentTarget) to enable multiple playbackRate bars
2460 var $bar = $(e.currentTarget),
2461 offset = $bar.offset(),
2462 x = e.pageX - offset.left,
2464 y = $bar.height() - e.pageY + offset.top,
2467 if(this.options.verticalPlaybackRate) {
2472 pbr = ratio * (this.options.maxPlaybackRate - this.options.minPlaybackRate) + this.options.minPlaybackRate;
2473 this.playbackRate(pbr);
2476 _updatePlaybackRate: function() {
2477 var pbr = this.options.playbackRate,
2478 ratio = (pbr - this.options.minPlaybackRate) / (this.options.maxPlaybackRate - this.options.minPlaybackRate);
2479 if(this.status.playbackRateEnabled) {
2480 if(this.css.jq.playbackRateBar.length) {
2481 this.css.jq.playbackRateBar.show();
2483 if(this.css.jq.playbackRateBarValue.length) {
2484 this.css.jq.playbackRateBarValue.show();
2485 this.css.jq.playbackRateBarValue[this.options.verticalPlaybackRate ? "height" : "width"]((ratio*100)+"%");
2488 if(this.css.jq.playbackRateBar.length) {
2489 this.css.jq.playbackRateBar.hide();
2491 if(this.css.jq.playbackRateBarValue.length) {
2492 this.css.jq.playbackRateBarValue.hide();
2496 repeat: function(event) { // Handle clicks on the repeat button
2497 var guiAction = typeof event === "object"; // Flags GUI click events so we know this was not a direct command, but an action taken by the user on the GUI.
2498 if(guiAction && this.options.useStateClassSkin && this.options.loop) {
2504 repeatOff: function() { // Handle clicks on the repeatOff button
2507 _loop: function(loop) {
2508 if(this.options.loop !== loop) {
2509 this.options.loop = loop;
2510 this._updateButtons();
2511 this._trigger($.jPlayer.event.repeat);
2515 // Options code adapted from ui.widget.js (1.8.7). Made changes so the key can use dot notation. To match previous getData solution in jPlayer 1.
2516 option: function(key, value) {
2519 // Enables use: options(). Returns a copy of options object
2520 if ( arguments.length === 0 ) {
2521 return $.extend( true, {}, this.options );
2524 if(typeof key === "string") {
2525 var keys = key.split(".");
2527 // Enables use: options("someOption") Returns a copy of the option. Supports dot notation.
2528 if(value === undefined) {
2530 var opt = $.extend(true, {}, this.options);
2531 for(var i = 0; i < keys.length; i++) {
2532 if(opt[keys[i]] !== undefined) {
2536 type: $.jPlayer.warning.OPTION_KEY,
2538 message: $.jPlayer.warningMsg.OPTION_KEY,
2539 hint: $.jPlayer.warningHint.OPTION_KEY
2547 // Enables use: options("someOptionObject", someObject}). Creates: {someOptionObject:someObject}
2548 // Enables use: options("someOption", someValue). Creates: {someOption:someValue}
2549 // Enables use: options("someOptionObject.someOption", someValue). Creates: {someOptionObject:{someOption:someValue}}
2554 for(var j = 0; j < keys.length; j++) {
2555 if(j < keys.length - 1) {
2557 opts = opts[keys[j]];
2559 opts[keys[j]] = value;
2564 // Otherwise enables use: options(optionObject). Uses original object (the key)
2566 this._setOptions(options);
2570 _setOptions: function(options) {
2572 $.each(options, function(key, value) { // This supports the 2 level depth that the options of jPlayer has. Would review if we ever need more depth.
2573 self._setOption(key, value);
2578 _setOption: function(key, value) {
2581 // The ability to set options is limited at this time.
2590 case "globalVolume" :
2591 this.options[key] = value;
2593 case "cssSelectorAncestor" :
2594 this._cssSelectorAncestor(value); // Set and refresh all associations for the new ancestor.
2596 case "cssSelector" :
2597 $.each(value, function(fn, cssSel) {
2598 self._cssSelector(fn, cssSel); // NB: The option is set inside this function, after further validity checks.
2601 case "playbackRate" :
2602 this.options[key] = value = this._limitValue(value, this.options.minPlaybackRate, this.options.maxPlaybackRate);
2603 if(this.html.used) {
2604 this._html_setProperty('playbackRate', value);
2606 this._updatePlaybackRate();
2608 case "defaultPlaybackRate" :
2609 this.options[key] = value = this._limitValue(value, this.options.minPlaybackRate, this.options.maxPlaybackRate);
2610 if(this.html.used) {
2611 this._html_setProperty('defaultPlaybackRate', value);
2613 this._updatePlaybackRate();
2615 case "minPlaybackRate" :
2616 this.options[key] = value = this._limitValue(value, 0.1, this.options.maxPlaybackRate - 0.1);
2617 this._updatePlaybackRate();
2619 case "maxPlaybackRate" :
2620 this.options[key] = value = this._limitValue(value, this.options.minPlaybackRate + 0.1, 16);
2621 this._updatePlaybackRate();
2624 if(this.options[key] !== value) { // if changed
2625 var wkv = $.jPlayer.nativeFeatures.fullscreen.used.webkitVideo;
2626 if(!wkv || wkv && !this.status.waitForPlay) {
2627 if(!wkv) { // No sensible way to unset option on these devices.
2628 this.options[key] = value;
2631 this._requestFullscreen();
2633 this._exitFullscreen();
2636 this._setOption("fullWindow", value);
2642 if(this.options[key] !== value) { // if changed
2643 this._removeUiClass();
2644 this.options[key] = value;
2645 this._refreshSize();
2649 if(!this.options.fullWindow && this.options[key].cssClass !== value.cssClass) {
2650 this._removeUiClass();
2652 this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed.
2653 this._refreshSize();
2656 if(this.options.fullWindow && this.options[key].cssClass !== value.cssClass) {
2657 this._removeUiClass();
2659 this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed.
2660 this._refreshSize();
2663 this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed.
2664 this._updateAutohide();
2669 case "remainingDuration" :
2670 this.options[key] = value;
2671 this._updateInterface();
2673 case "toggleDuration" :
2674 this.options[key] = value;
2676 case "nativeVideoControls" :
2677 this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed.
2678 this.status.nativeVideoControls = this._uaBlocklist(this.options.nativeVideoControls);
2679 this._restrictNativeVideoControls();
2680 this._updateNativeVideoControls();
2682 case "noFullWindow" :
2683 this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed.
2684 this.status.nativeVideoControls = this._uaBlocklist(this.options.nativeVideoControls); // Need to check again as noFullWindow can depend on this flag and the restrict() can override it.
2685 this.status.noFullWindow = this._uaBlocklist(this.options.noFullWindow);
2686 this._restrictNativeVideoControls();
2687 this._updateButtons();
2690 this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed.
2691 this.status.noVolume = this._uaBlocklist(this.options.noVolume);
2692 this._updateVolume();
2695 case "emulateHtml" :
2696 if(this.options[key] !== value) { // To avoid multiple event handlers being created, if true already.
2697 this.options[key] = value;
2699 this._emulateHtmlBridge();
2701 this._destroyHtmlBridge();
2706 this.options[key] = $.extend({}, this.options[key], value); // store a merged copy of it, incase not all properties changed.
2709 this.options[key] = value;
2710 if(!value && this === $.jPlayer.focus) {
2711 $.jPlayer.focus = null;
2714 case "keyBindings" :
2715 this.options[key] = $.extend(true, {}, this.options[key], value); // store a merged DEEP copy of it, incase not all properties changed.
2717 case "audioFullScreen" :
2718 this.options[key] = value;
2721 this.options[key] = value;
2727 // End of: (Options code adapted from ui.widget.js)
2729 _refreshSize: function() {
2730 this._setSize(); // update status and jPlayer element size
2731 this._addUiClass(); // update the ui class
2732 this._updateSize(); // update internal sizes
2733 this._updateButtons();
2734 this._updateAutohide();
2735 this._trigger($.jPlayer.event.resize);
2737 _setSize: function() {
2738 // Determine the current size from the options
2739 if(this.options.fullWindow) {
2740 this.status.width = this.options.sizeFull.width;
2741 this.status.height = this.options.sizeFull.height;
2742 this.status.cssClass = this.options.sizeFull.cssClass;
2744 this.status.width = this.options.size.width;
2745 this.status.height = this.options.size.height;
2746 this.status.cssClass = this.options.size.cssClass;
2749 // Set the size of the jPlayer area.
2750 this.element.css({'width': this.status.width, 'height': this.status.height});
2752 _addUiClass: function() {
2753 if(this.ancestorJq.length) {
2754 this.ancestorJq.addClass(this.status.cssClass);
2757 _removeUiClass: function() {
2758 if(this.ancestorJq.length) {
2759 this.ancestorJq.removeClass(this.status.cssClass);
2762 _updateSize: function() {
2763 // The poster uses show/hide so can simply resize it.
2764 this.internal.poster.jq.css({'width': this.status.width, 'height': this.status.height});
2766 // Video html or flash resized if necessary at this time, or if native video controls being used.
2767 if(!this.status.waitForPlay && this.html.active && this.status.video || this.html.video.available && this.html.used && this.status.nativeVideoControls) {
2768 this.internal.video.jq.css({'width': this.status.width, 'height': this.status.height});
2770 else if(!this.status.waitForPlay && this.flash.active && this.status.video) {
2771 this.internal.flash.jq.css({'width': this.status.width, 'height': this.status.height});
2774 _updateAutohide: function() {
2776 event = "mousemove.jPlayer",
2777 namespace = ".jPlayerAutohide",
2778 eventType = event + namespace,
2779 handler = function(event) {
2782 if(typeof self.internal.mouse !== "undefined") {
2783 //get the change from last position to this position
2784 deltaX = self.internal.mouse.x - event.pageX;
2785 deltaY = self.internal.mouse.y - event.pageY;
2786 moved = (Math.floor(deltaX) > 0) || (Math.floor(deltaY)>0);
2790 // store current position for next method execution
2791 self.internal.mouse = {
2795 // if mouse has been actually moved, do the gui fadeIn/fadeOut
2797 self.css.jq.gui.fadeIn(self.options.autohide.fadeIn, function() {
2798 clearTimeout(self.internal.autohideId);
2799 self.internal.autohideId = setTimeout( function() {
2800 self.css.jq.gui.fadeOut(self.options.autohide.fadeOut);
2801 }, self.options.autohide.hold);
2806 if(this.css.jq.gui.length) {
2808 // End animations first so that its callback is executed now.
2809 // Otherwise an in progress fadeIn animation still has the callback to fadeOut again.
2810 this.css.jq.gui.stop(true, true);
2812 // Removes the fadeOut operation from the fadeIn callback.
2813 clearTimeout(this.internal.autohideId);
2815 delete this.internal.mouse;
2817 this.element.unbind(namespace);
2818 this.css.jq.gui.unbind(namespace);
2820 if(!this.status.nativeVideoControls) {
2821 if(this.options.fullWindow && this.options.autohide.full || !this.options.fullWindow && this.options.autohide.restored) {
2822 this.element.bind(eventType, handler);
2823 this.css.jq.gui.bind(eventType, handler);
2824 this.css.jq.gui.hide();
2826 this.css.jq.gui.show();
2829 this.css.jq.gui.hide();
2833 fullScreen: function(event) {
2834 var guiAction = typeof event === "object"; // Flags GUI click events so we know this was not a direct command, but an action taken by the user on the GUI.
2835 if(guiAction && this.options.useStateClassSkin && this.options.fullScreen) {
2836 this._setOption("fullScreen", false);
2838 this._setOption("fullScreen", true);
2841 restoreScreen: function() {
2842 this._setOption("fullScreen", false);
2844 _fullscreenAddEventListeners: function() {
2846 fs = $.jPlayer.nativeFeatures.fullscreen;
2848 if(fs.api.fullscreenEnabled) {
2849 if(fs.event.fullscreenchange) {
2850 // Create the event handler function and store it for removal.
2851 if(typeof this.internal.fullscreenchangeHandler !== 'function') {
2852 this.internal.fullscreenchangeHandler = function() {
2853 self._fullscreenchange();
2856 document.addEventListener(fs.event.fullscreenchange, this.internal.fullscreenchangeHandler, false);
2858 // No point creating handler for fullscreenerror.
2859 // Either logic avoids fullscreen occurring (w3c/moz), or their is no event on the browser (webkit).
2862 _fullscreenRemoveEventListeners: function() {
2863 var fs = $.jPlayer.nativeFeatures.fullscreen;
2864 if(this.internal.fullscreenchangeHandler) {
2865 document.removeEventListener(fs.event.fullscreenchange, this.internal.fullscreenchangeHandler, false);
2868 _fullscreenchange: function() {
2869 // If nothing is fullscreen, then we cannot be in fullscreen mode.
2870 if(this.options.fullScreen && !$.jPlayer.nativeFeatures.fullscreen.api.fullscreenElement()) {
2871 this._setOption("fullScreen", false);
2874 _requestFullscreen: function() {
2875 // Either the container or the jPlayer div
2876 var e = this.ancestorJq.length ? this.ancestorJq[0] : this.element[0],
2877 fs = $.jPlayer.nativeFeatures.fullscreen;
2879 // This method needs the video element. For iOS and Android.
2880 if(fs.used.webkitVideo) {
2881 e = this.htmlElement.video;
2884 if(fs.api.fullscreenEnabled) {
2885 fs.api.requestFullscreen(e);
2888 _exitFullscreen: function() {
2890 var fs = $.jPlayer.nativeFeatures.fullscreen,
2893 // This method needs the video element. For iOS and Android.
2894 if(fs.used.webkitVideo) {
2895 e = this.htmlElement.video;
2898 if(fs.api.fullscreenEnabled) {
2899 fs.api.exitFullscreen(e);
2902 _html_initMedia: function(media) {
2903 // Remove any existing track elements
2904 var $media = $(this.htmlElement.media).empty();
2906 // Create any track elements given with the media, as an Array of track Objects.
2907 $.each(media.track || [], function(i,v) {
2908 var track = document.createElement('track');
2909 track.setAttribute("kind", v.kind ? v.kind : "");
2910 track.setAttribute("src", v.src ? v.src : "");
2911 track.setAttribute("srclang", v.srclang ? v.srclang : "");
2912 track.setAttribute("label", v.label ? v.label : "");
2914 track.setAttribute("default", v.def);
2916 $media.append(track);
2919 this.htmlElement.media.src = this.status.src;
2921 if(this.options.preload !== 'none') {
2922 this._html_load(); // See function for comments
2924 this._trigger($.jPlayer.event.timeupdate); // The flash generates this event for its solution.
2926 _html_setFormat: function(media) {
2928 // Always finds a format due to checks in setMedia()
2929 $.each(this.formats, function(priority, format) {
2930 if(self.html.support[format] && media[format]) {
2931 self.status.src = media[format];
2932 self.status.format[format] = true;
2933 self.status.formatType = format;
2938 _html_setAudio: function(media) {
2939 this._html_setFormat(media);
2940 this.htmlElement.media = this.htmlElement.audio;
2941 this._html_initMedia(media);
2943 _html_setVideo: function(media) {
2944 this._html_setFormat(media);
2945 if(this.status.nativeVideoControls) {
2946 this.htmlElement.video.poster = this._validString(media.poster) ? media.poster : "";
2948 this.htmlElement.media = this.htmlElement.video;
2949 this._html_initMedia(media);
2951 _html_resetMedia: function() {
2952 if(this.htmlElement.media) {
2953 if(this.htmlElement.media.id === this.internal.video.id && !this.status.nativeVideoControls) {
2954 this.internal.video.jq.css({'width':'0px', 'height':'0px'});
2956 this.htmlElement.media.pause();
2959 _html_clearMedia: function() {
2960 if(this.htmlElement.media) {
2961 this.htmlElement.media.src = "about:blank";
2962 // The following load() is only required for Firefox 3.6 (PowerMacs).
2963 // Recent HTMl5 browsers only require the src change. Due to changes in W3C spec and load() effect.
2964 this.htmlElement.media.load(); // Stops an old, "in progress" download from continuing the download. Triggers the loadstart, error and emptied events, due to the empty src. Also an abort event if a download was in progress.
2967 _html_load: function() {
2968 // This function remains to allow the early HTML5 browsers to work, such as Firefox 3.6
2969 // A change in the W3C spec for the media.load() command means that this is no longer necessary.
2970 // This command should be removed and actually causes minor undesirable effects on some browsers. Such as loading the whole file and not only the metadata.
2971 if(this.status.waitForLoad) {
2972 this.status.waitForLoad = false;
2973 this.htmlElement.media.load();
2975 clearTimeout(this.internal.htmlDlyCmdId);
2977 _html_play: function(time) {
2979 media = this.htmlElement.media;
2981 this.androidFix.pause = false; // Cancel the pause fix.
2983 this._html_load(); // Loads if required and clears any delayed commands.
2985 // Setup the Android Fix.
2986 if(this.androidFix.setMedia) {
2987 this.androidFix.play = true;
2988 this.androidFix.time = time;
2990 } else if(!isNaN(time)) {
2992 // Attempt to play it, since iOS has been ignoring commands
2993 if(this.internal.cmdsIgnored) {
2998 // !media.seekable is for old HTML5 browsers, like Firefox 3.6.
2999 // Checking seekable.length is important for iOS6 to work with setMedia().play(time)
3000 if(!media.seekable || typeof media.seekable === "object" && media.seekable.length > 0) {
3001 media.currentTime = time;
3007 this.internal.htmlDlyCmdId = setTimeout(function() {
3010 return; // Cancel execution and wait for the delayed command.
3015 this._html_checkWaitForPlay();
3017 _html_pause: function(time) {
3019 media = this.htmlElement.media;
3021 this.androidFix.play = false; // Cancel the play fix.
3023 if(time > 0) { // We do not want the stop() command, which does pause(0), causing a load operation.
3024 this._html_load(); // Loads if required and clears any delayed commands.
3026 clearTimeout(this.internal.htmlDlyCmdId);
3029 // Order of these commands is important for Safari (Win) and IE9. Pause then change currentTime.
3032 // Setup the Android Fix.
3033 if(this.androidFix.setMedia) {
3034 this.androidFix.pause = true;
3035 this.androidFix.time = time;
3037 } else if(!isNaN(time)) {
3039 if(!media.seekable || typeof media.seekable === "object" && media.seekable.length > 0) {
3040 media.currentTime = time;
3045 this.internal.htmlDlyCmdId = setTimeout(function() {
3048 return; // Cancel execution and wait for the delayed command.
3051 if(time > 0) { // Avoids a setMedia() followed by stop() or pause(0) hiding the video play button.
3052 this._html_checkWaitForPlay();
3055 _html_playHead: function(percent) {
3057 media = this.htmlElement.media;
3059 this._html_load(); // Loads if required and clears any delayed commands.
3061 // This playHead() method needs a refactor to apply the android fix.
3064 if(typeof media.seekable === "object" && media.seekable.length > 0) {
3065 media.currentTime = percent * media.seekable.end(media.seekable.length-1) / 100;
3066 } else if(media.duration > 0 && !isNaN(media.duration)) {
3067 media.currentTime = percent * media.duration / 100;
3072 this.internal.htmlDlyCmdId = setTimeout(function() {
3073 self.playHead(percent);
3075 return; // Cancel execution and wait for the delayed command.
3077 if(!this.status.waitForLoad) {
3078 this._html_checkWaitForPlay();
3081 _html_checkWaitForPlay: function() {
3082 if(this.status.waitForPlay) {
3083 this.status.waitForPlay = false;
3084 if(this.css.jq.videoPlay.length) {
3085 this.css.jq.videoPlay.hide();
3087 if(this.status.video) {
3088 this.internal.poster.jq.hide();
3089 this.internal.video.jq.css({'width': this.status.width, 'height': this.status.height});
3093 _html_setProperty: function(property, value) {
3094 if(this.html.audio.available) {
3095 this.htmlElement.audio[property] = value;
3097 if(this.html.video.available) {
3098 this.htmlElement.video[property] = value;
3101 _aurora_setAudio: function(media) {
3104 // Always finds a format due to checks in setMedia()
3105 $.each(this.formats, function(priority, format) {
3106 if(self.aurora.support[format] && media[format]) {
3107 self.status.src = media[format];
3108 self.status.format[format] = true;
3109 self.status.formatType = format;
3115 this.aurora.player = new AV.Player.fromURL(this.status.src);
3116 this._addAuroraEventListeners(this.aurora.player, this.aurora);
3118 if(this.options.preload === 'auto') {
3119 this._aurora_load();
3120 this.status.waitForLoad = false;
3123 _aurora_resetMedia: function() {
3124 if (this.aurora.player) {
3125 this.aurora.player.stop();
3128 _aurora_clearMedia: function() {
3129 // Nothing to clear.
3131 _aurora_load: function() {
3132 if(this.status.waitForLoad) {
3133 this.status.waitForLoad = false;
3134 this.aurora.player.preload();
3137 _aurora_play: function(time) {
3138 if (!this.status.waitForLoad) {
3140 this.aurora.player.seek(time);
3143 if (!this.aurora.player.playing) {
3144 this.aurora.player.play();
3146 this.status.waitForLoad = false;
3147 this._aurora_checkWaitForPlay();
3149 // No event from the player, update UI now.
3150 this._updateButtons(true);
3151 this._trigger($.jPlayer.event.play);
3153 _aurora_pause: function(time) {
3155 this.aurora.player.seek(time * 1000);
3157 this.aurora.player.pause();
3159 if(time > 0) { // Avoids a setMedia() followed by stop() or pause(0) hiding the video play button.
3160 this._aurora_checkWaitForPlay();
3163 // No event from the player, update UI now.
3164 this._updateButtons(false);
3165 this._trigger($.jPlayer.event.pause);
3167 _aurora_playHead: function(percent) {
3168 if(this.aurora.player.duration > 0) {
3169 // The seek() sould be in milliseconds, but the only codec that works with seek (aac.js) uses seconds.
3170 this.aurora.player.seek(percent * this.aurora.player.duration / 100); // Using seconds
3173 if(!this.status.waitForLoad) {
3174 this._aurora_checkWaitForPlay();
3177 _aurora_checkWaitForPlay: function() {
3178 if(this.status.waitForPlay) {
3179 this.status.waitForPlay = false;
3182 _aurora_volume: function(v) {
3183 this.aurora.player.volume = v * 100;
3185 _aurora_mute: function(m) {
3187 this.aurora.properties.lastvolume = this.aurora.player.volume;
3188 this.aurora.player.volume = 0;
3190 this.aurora.player.volume = this.aurora.properties.lastvolume;
3192 this.aurora.properties.muted = m;
3194 _flash_setAudio: function(media) {
3197 // Always finds a format due to checks in setMedia()
3198 $.each(this.formats, function(priority, format) {
3199 if(self.flash.support[format] && media[format]) {
3203 self._getMovie().fl_setAudio_m4a(media[format]);
3206 self._getMovie().fl_setAudio_mp3(media[format]);
3209 self._getMovie().fl_setAudio_rtmp(media[format]);
3212 self.status.src = media[format];
3213 self.status.format[format] = true;
3214 self.status.formatType = format;
3219 if(this.options.preload === 'auto') {
3221 this.status.waitForLoad = false;
3223 } catch(err) { this._flashError(err); }
3225 _flash_setVideo: function(media) {
3228 // Always finds a format due to checks in setMedia()
3229 $.each(this.formats, function(priority, format) {
3230 if(self.flash.support[format] && media[format]) {
3234 self._getMovie().fl_setVideo_m4v(media[format]);
3237 self._getMovie().fl_setVideo_rtmp(media[format]);
3240 self.status.src = media[format];
3241 self.status.format[format] = true;
3242 self.status.formatType = format;
3247 if(this.options.preload === 'auto') {
3249 this.status.waitForLoad = false;
3251 } catch(err) { this._flashError(err); }
3253 _flash_resetMedia: function() {
3254 this.internal.flash.jq.css({'width':'0px', 'height':'0px'}); // Must do via CSS as setting attr() to zero causes a jQuery error in IE.
3255 this._flash_pause(NaN);
3257 _flash_clearMedia: function() {
3259 this._getMovie().fl_clearMedia();
3260 } catch(err) { this._flashError(err); }
3262 _flash_load: function() {
3264 this._getMovie().fl_load();
3265 } catch(err) { this._flashError(err); }
3266 this.status.waitForLoad = false;
3268 _flash_play: function(time) {
3270 this._getMovie().fl_play(time);
3271 } catch(err) { this._flashError(err); }
3272 this.status.waitForLoad = false;
3273 this._flash_checkWaitForPlay();
3275 _flash_pause: function(time) {
3277 this._getMovie().fl_pause(time);
3278 } catch(err) { this._flashError(err); }
3279 if(time > 0) { // Avoids a setMedia() followed by stop() or pause(0) hiding the video play button.
3280 this.status.waitForLoad = false;
3281 this._flash_checkWaitForPlay();
3284 _flash_playHead: function(p) {
3286 this._getMovie().fl_play_head(p);
3287 } catch(err) { this._flashError(err); }
3288 if(!this.status.waitForLoad) {
3289 this._flash_checkWaitForPlay();
3292 _flash_checkWaitForPlay: function() {
3293 if(this.status.waitForPlay) {
3294 this.status.waitForPlay = false;
3295 if(this.css.jq.videoPlay.length) {
3296 this.css.jq.videoPlay.hide();
3298 if(this.status.video) {
3299 this.internal.poster.jq.hide();
3300 this.internal.flash.jq.css({'width': this.status.width, 'height': this.status.height});
3304 _flash_volume: function(v) {
3306 this._getMovie().fl_volume(v);
3307 } catch(err) { this._flashError(err); }
3309 _flash_mute: function(m) {
3311 this._getMovie().fl_mute(m);
3312 } catch(err) { this._flashError(err); }
3314 _getMovie: function() {
3315 return document[this.internal.flash.id];
3317 _getFlashPluginVersion: function() {
3319 // _getFlashPluginVersion() code influenced by:
3320 // - FlashReplace 1.01: http://code.google.com/p/flashreplace/
3321 // - SWFObject 2.2: http://code.google.com/p/swfobject/
3325 if(window.ActiveXObject) {
3327 flash = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
3328 if (flash) { // flash will return null when ActiveX is disabled
3329 var v = flash.GetVariable("$version");
3331 v = v.split(" ")[1].split(",");
3332 version = parseInt(v[0], 10) + "." + parseInt(v[1], 10);
3337 else if(navigator.plugins && navigator.mimeTypes.length > 0) {
3338 flash = navigator.plugins["Shockwave Flash"];
3340 version = navigator.plugins["Shockwave Flash"].description.replace(/.*\s(\d+\.\d+).*/, "$1");
3343 return version * 1; // Converts to a number
3345 _checkForFlash: function (version) {
3346 var flashOk = false;
3347 if(this._getFlashPluginVersion() >= version) {
3352 _validString: function(url) {
3353 return (url && typeof url === "string"); // Empty strings return false
3355 _limitValue: function(value, min, max) {
3356 return (value < min) ? min : ((value > max) ? max : value);
3358 _urlNotSetError: function(context) {
3360 type: $.jPlayer.error.URL_NOT_SET,
3362 message: $.jPlayer.errorMsg.URL_NOT_SET,
3363 hint: $.jPlayer.errorHint.URL_NOT_SET
3366 _flashError: function(error) {
3368 if(!this.internal.ready) {
3369 errorType = "FLASH";
3371 errorType = "FLASH_DISABLED";
3374 type: $.jPlayer.error[errorType],
3375 context: this.internal.flash.swf,
3376 message: $.jPlayer.errorMsg[errorType] + error.message,
3377 hint: $.jPlayer.errorHint[errorType]
3379 // Allow the audio player to recover if display:none and then shown again, or with position:fixed on Firefox.
3380 // This really only affects audio in a media player, as an audio player could easily move the jPlayer element away from such issues.
3381 this.internal.flash.jq.css({'width':'1px', 'height':'1px'});
3383 _error: function(error) {
3384 this._trigger($.jPlayer.event.error, error);
3385 if(this.options.errorAlerts) {
3386 this._alert("Error!" + (error.message ? "\n" + error.message : "") + (error.hint ? "\n" + error.hint : "") + "\nContext: " + error.context);
3389 _warning: function(warning) {
3390 this._trigger($.jPlayer.event.warning, undefined, warning);
3391 if(this.options.warningAlerts) {
3392 this._alert("Warning!" + (warning.message ? "\n" + warning.message : "") + (warning.hint ? "\n" + warning.hint : "") + "\nContext: " + warning.context);
3395 _alert: function(message) {
3396 var msg = "jPlayer " + this.version.script + " : id='" + this.internal.self.id +"' : " + message;
3397 if(!this.options.consoleAlerts) {
3399 } else if(window.console && window.console.log) {
3400 window.console.log(msg);
3403 _emulateHtmlBridge: function() {
3406 // Emulate methods on jPlayer's DOM element.
3407 $.each( $.jPlayer.emulateMethods.split(/\s+/g), function(i, name) {
3408 self.internal.domNode[name] = function(arg) {
3414 // Bubble jPlayer events to its DOM element.
3415 $.each($.jPlayer.event, function(eventName,eventType) {
3416 var nativeEvent = true;
3417 $.each( $.jPlayer.reservedEvent.split(/\s+/g), function(i, name) {
3418 if(name === eventName) {
3419 nativeEvent = false;
3424 self.element.bind(eventType + ".jPlayer.jPlayerHtml", function() { // With .jPlayer & .jPlayerHtml namespaces.
3425 self._emulateHtmlUpdate();
3426 var domEvent = document.createEvent("Event");
3427 domEvent.initEvent(eventName, false, true);
3428 self.internal.domNode.dispatchEvent(domEvent);
3431 // The error event would require a special case
3434 // IE9 has a readyState property on all elements. The document should have it, but all (except media) elements inherit it in IE9. This conflicts with Popcorn, which polls the readyState.
3436 _emulateHtmlUpdate: function() {
3439 $.each( $.jPlayer.emulateStatus.split(/\s+/g), function(i, name) {
3440 self.internal.domNode[name] = self.status[name];
3442 $.each( $.jPlayer.emulateOptions.split(/\s+/g), function(i, name) {
3443 self.internal.domNode[name] = self.options[name];
3446 _destroyHtmlBridge: function() {
3449 // Bridge event handlers are also removed by destroy() through .jPlayer namespace.
3450 this.element.unbind(".jPlayerHtml"); // Remove all event handlers created by the jPlayer bridge. So you can change the emulateHtml option.
3452 // Remove the methods and properties
3453 var emulated = $.jPlayer.emulateMethods + " " + $.jPlayer.emulateStatus + " " + $.jPlayer.emulateOptions;
3454 $.each( emulated.split(/\s+/g), function(i, name) {
3455 delete self.internal.domNode[name];
3462 FLASH_DISABLED: "e_flash_disabled",
3463 NO_SOLUTION: "e_no_solution",
3464 NO_SUPPORT: "e_no_support",
3466 URL_NOT_SET: "e_url_not_set",
3467 VERSION: "e_version"
3470 $.jPlayer.errorMsg = {
3471 FLASH: "jPlayer's Flash fallback is not configured correctly, or a command was issued before the jPlayer Ready event. Details: ", // Used in: _flashError()
3472 FLASH_DISABLED: "jPlayer's Flash fallback has been disabled by the browser due to the CSS rules you have used. Details: ", // Used in: _flashError()
3473 NO_SOLUTION: "No solution can be found by jPlayer in this browser. Neither HTML nor Flash can be used.", // Used in: _init()
3474 NO_SUPPORT: "It is not possible to play any media format provided in setMedia() on this browser using your current options.", // Used in: setMedia()
3475 URL: "Media URL could not be loaded.", // Used in: jPlayerFlashEvent() and _addHtmlEventListeners()
3476 URL_NOT_SET: "Attempt to issue media playback commands, while no media url is set.", // Used in: load(), play(), pause(), stop() and playHead()
3477 VERSION: "jPlayer " + $.jPlayer.prototype.version.script + " needs Jplayer.swf version " + $.jPlayer.prototype.version.needFlash + " but found " // Used in: jPlayerReady()
3480 $.jPlayer.errorHint = {
3481 FLASH: "Check your swfPath option and that Jplayer.swf is there.",
3482 FLASH_DISABLED: "Check that you have not display:none; the jPlayer entity or any ancestor.",
3483 NO_SOLUTION: "Review the jPlayer options: support and supplied.",
3484 NO_SUPPORT: "Video or audio formats defined in the supplied option are missing.",
3485 URL: "Check media URL is valid.",
3486 URL_NOT_SET: "Use setMedia() to set the media URL.",
3487 VERSION: "Update jPlayer files."
3490 $.jPlayer.warning = {
3491 CSS_SELECTOR_COUNT: "e_css_selector_count",
3492 CSS_SELECTOR_METHOD: "e_css_selector_method",
3493 CSS_SELECTOR_STRING: "e_css_selector_string",
3494 OPTION_KEY: "e_option_key"
3497 $.jPlayer.warningMsg = {
3498 CSS_SELECTOR_COUNT: "The number of css selectors found did not equal one: ",
3499 CSS_SELECTOR_METHOD: "The methodName given in jPlayer('cssSelector') is not a valid jPlayer method.",
3500 CSS_SELECTOR_STRING: "The methodCssSelector given in jPlayer('cssSelector') is not a String or is empty.",
3501 OPTION_KEY: "The option requested in jPlayer('option') is undefined."
3504 $.jPlayer.warningHint = {
3505 CSS_SELECTOR_COUNT: "Check your css selector and the ancestor.",
3506 CSS_SELECTOR_METHOD: "Check your method name.",
3507 CSS_SELECTOR_STRING: "Check your css selector is a string.",
3508 OPTION_KEY: "Check your option name."