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