vendor: 15,833 bytes, lines 1-623
1/** 2 * @license 3 * Video.js 6.6.3 <http://videojs.com/> 4 * Copyright Brightcove, Inc. <https://www.brightcove.com/> 5 * Available under Apache License Version 2.0 6 * <https://github.com/videojs/video.js/blob/master/LICENSE> 7 * 8 * Includes vtt.js <https://github.com/mozilla/vtt.js> 9 * Available under Apache License Version 2.0 10 * <https://github.com/mozilla/vtt.js/blob/master/LICENSE> 11 */ 12 13(function (global, factory) { 14 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 15 typeof define === 'function' && define.amd ? define(factory) : 16 (global.videojs = factory()); 17}(this, (function () { 18 19var version = "6.6.3"; 20 21var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; 22 23 24 25 26 27function createCommonjsModule(fn, module) { 28 return module = { exports: {} }, fn(module, module.exports), module.exports; 29} 30 31var win; 32 33if (typeof window !== "undefined") { 34 win = window; 35} else if (typeof commonjsGlobal !== "undefined") { 36 win = commonjsGlobal; 37} else if (typeof self !== "undefined"){ 38 win = self; 39} else { 40 win = {}; 41} 42 43var window_1 = win; 44 45var empty = {}; 46 47 48var empty$1 = (Object.freeze || Object)({ 49 'default': empty 50}); 51 52var minDoc = ( empty$1 && empty ) || empty$1; 53 54var topLevel = typeof commonjsGlobal !== 'undefined' ? commonjsGlobal : 55 typeof window !== 'undefined' ? window : {}; 56 57 58var doccy; 59 60if (typeof document !== 'undefined') { 61 doccy = document; 62} else { 63 doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4']; 64 65 if (!doccy) { 66 doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc; 67 } 68} 69 70var document_1 = doccy; 71 72/** 73 * @file browser.js 74 * @module browser 75 */ 76var USER_AGENT = window_1.navigator && window_1.navigator.userAgent || ''; 77var webkitVersionMap = /AppleWebKit\/([\d.]+)/i.exec(USER_AGENT); 78var appleWebkitVersion = webkitVersionMap ? parseFloat(webkitVersionMap.pop()) : null; 79 80/* 81 * Device is an iPhone 82 * 83 * @type {Boolean} 84 * @constant 85 * @private 86 */ 87var IS_IPAD = /iPad/i.test(USER_AGENT); 88 89// The Facebook app's UIWebView identifies as both an iPhone and iPad, so 90// to identify iPhones, we need to exclude iPads. 91// http://artsy.github.io/blog/2012/10/18/the-perils-of-ios-user-agent-sniffing/ 92var IS_IPHONE = /iPhone/i.test(USER_AGENT) && !IS_IPAD; 93var IS_IPOD = /iPod/i.test(USER_AGENT); 94var IS_IOS = IS_IPHONE || IS_IPAD || IS_IPOD; 95 96var IOS_VERSION = function () { 97 var match = USER_AGENT.match(/OS (\d+)_/i); 98 99 if (match && match[1]) { 100 return match[1]; 101 } 102 return null; 103}(); 104 105var IS_ANDROID = /Android/i.test(USER_AGENT); 106var ANDROID_VERSION = function () { 107 // This matches Android Major.Minor.Patch versions 108 // ANDROID_VERSION is Major.Minor as a Number, if Minor isn't available, then only Major is returned 109 var match = USER_AGENT.match(/Android (\d+)(?:\.(\d+))?(?:\.(\d+))*/i); 110 111 if (!match) { 112 return null; 113 } 114 115 var major = match[1] && parseFloat(match[1]); 116 var minor = match[2] && parseFloat(match[2]); 117 118 if (major && minor) { 119 return parseFloat(match[1] + '.' + match[2]); 120 } else if (major) { 121 return major; 122 } 123 return null; 124}(); 125 126// Old Android is defined as Version older than 2.3, and requiring a webkit version of the android browser 127var IS_OLD_ANDROID = IS_ANDROID && /webkit/i.test(USER_AGENT) && ANDROID_VERSION < 2.3; 128var IS_NATIVE_ANDROID = IS_ANDROID && ANDROID_VERSION < 5 && appleWebkitVersion < 537; 129 130var IS_FIREFOX = /Firefox/i.test(USER_AGENT); 131var IS_EDGE = /Edge/i.test(USER_AGENT); 132var IS_CHROME = !IS_EDGE && /Chrome/i.test(USER_AGENT); 133var CHROME_VERSION = function () { 134 var match = USER_AGENT.match(/Chrome\/(\d+)/); 135 136 if (match && match[1]) { 137 return parseFloat(match[1]); 138 } 139 return null; 140}(); 141var IS_IE8 = /MSIE\s8\.0/.test(USER_AGENT); 142var IE_VERSION = function () { 143 var result = /MSIE\s(\d+)\.\d/.exec(USER_AGENT); 144 var version = result && parseFloat(result[1]); 145 146 if (!version && /Trident\/7.0/i.test(USER_AGENT) && /rv:11.0/.test(USER_AGENT)) { 147 // IE 11 has a different user agent string than other IE versions 148 version = 11.0; 149 } 150 151 return version; 152}(); 153 154var IS_SAFARI = /Safari/i.test(USER_AGENT) && !IS_CHROME && !IS_ANDROID && !IS_EDGE; 155var IS_ANY_SAFARI = IS_SAFARI || IS_IOS; 156 157var TOUCH_ENABLED = isReal() && ('ontouchstart' in window_1 || window_1.DocumentTouch && window_1.document instanceof window_1.DocumentTouch); 158 159var BACKGROUND_SIZE_SUPPORTED = isReal() && 'backgroundSize' in window_1.document.createElement('video').style; 160 161var browser = (Object.freeze || Object)({ 162 IS_IPAD: IS_IPAD, 163 IS_IPHONE: IS_IPHONE, 164 IS_IPOD: IS_IPOD, 165 IS_IOS: IS_IOS, 166 IOS_VERSION: IOS_VERSION, 167 IS_ANDROID: IS_ANDROID, 168 ANDROID_VERSION: ANDROID_VERSION, 169 IS_OLD_ANDROID: IS_OLD_ANDROID, 170 IS_NATIVE_ANDROID: IS_NATIVE_ANDROID, 171 IS_FIREFOX: IS_FIREFOX, 172 IS_EDGE: IS_EDGE, 173 IS_CHROME: IS_CHROME, 174 CHROME_VERSION: CHROME_VERSION, 175 IS_IE8: IS_IE8, 176 IE_VERSION: IE_VERSION, 177 IS_SAFARI: IS_SAFARI, 178 IS_ANY_SAFARI: IS_ANY_SAFARI, 179 TOUCH_ENABLED: TOUCH_ENABLED, 180 BACKGROUND_SIZE_SUPPORTED: BACKGROUND_SIZE_SUPPORTED 181}); 182 183var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { 184 return typeof obj; 185} : function (obj) { 186 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; 187}; 188 189 190 191 192 193 194 195 196 197 198 199var classCallCheck = function (instance, Constructor) { 200 if (!(instance instanceof Constructor)) { 201 throw new TypeError("Cannot call a class as a function"); 202 } 203}; 204 205 206 207 208 209 210 211 212 213 214 215var inherits = function (subClass, superClass) { 216 if (typeof superClass !== "function" && superClass !== null) { 217 throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); 218 } 219 220 subClass.prototype = Object.create(superClass && superClass.prototype, { 221 constructor: { 222 value: subClass, 223 enumerable: false, 224 writable: true, 225 configurable: true 226 } 227 }); 228 if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; 229}; 230 231 232 233 234 235 236 237 238 239 240 241var possibleConstructorReturn = function (self, call) { 242 if (!self) { 243 throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); 244 } 245 246 return call && (typeof call === "object" || typeof call === "function") ? call : self; 247}; 248 249 250 251 252 253 254 255 256 257 258 259var taggedTemplateLiteralLoose = function (strings, raw) { 260 strings.raw = raw; 261 return strings; 262}; 263 264/** 265 * @file obj.js 266 * @module obj 267 */ 268 269/** 270 * @callback obj:EachCallback 271 * 272 * @param {Mixed} value 273 * The current key for the object that is being iterated over. 274 * 275 * @param {string} key 276 * The current key-value for object that is being iterated over 277 */ 278 279/** 280 * @callback obj:ReduceCallback 281 * 282 * @param {Mixed} accum 283 * The value that is accumulating over the reduce loop. 284 * 285 * @param {Mixed} value 286 * The current key for the object that is being iterated over. 287 * 288 * @param {string} key 289 * The current key-value for object that is being iterated over 290 * 291 * @return {Mixed} 292 * The new accumulated value. 293 */ 294var toString = Object.prototype.toString; 295 296/** 297 * Get the keys of an Object 298 * 299 * @param {Object} 300 * The Object to get the keys from 301 * 302 * @return {string[]} 303 * An array of the keys from the object. Returns an empty array if the 304 * object passed in was invalid or had no keys. 305 * 306 * @private 307 */ 308var keys = function keys(object) { 309 return isObject(object) ? Object.keys(object) : []; 310}; 311 312/** 313 * Array-like iteration for objects. 314 * 315 * @param {Object} object 316 * The object to iterate over 317 * 318 * @param {obj:EachCallback} fn 319 * The callback function which is called for each key in the object. 320 */ 321function each(object, fn) { 322 keys(object).forEach(function (key) { 323 return fn(object[key], key); 324 }); 325} 326 327/** 328 * Array-like reduce for objects. 329 * 330 * @param {Object} object 331 * The Object that you want to reduce. 332 * 333 * @param {Function} fn 334 * A callback function which is called for each key in the object. It 335 * receives the accumulated value and the per-iteration value and key 336 * as arguments. 337 * 338 * @param {Mixed} [initial = 0] 339 * Starting value 340 * 341 * @return {Mixed} 342 * The final accumulated value. 343 */ 344function reduce(object, fn) { 345 var initial = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; 346 347 return keys(object).reduce(function (accum, key) { 348 return fn(accum, object[key], key); 349 }, initial); 350} 351 352/** 353 * Object.assign-style object shallow merge/extend. 354 * 355 * @param {Object} target 356 * @param {Object} ...sources 357 * @return {Object} 358 */ 359function assign(target) { 360 for (var _len = arguments.length, sources = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 361 sources[_key - 1] = arguments[_key]; 362 } 363 364 if (Object.assign) { 365 return Object.assign.apply(Object, [target].concat(sources)); 366 } 367 368 sources.forEach(function (source) { 369 if (!source) { 370 return; 371 } 372 373 each(source, function (value, key) { 374 target[key] = value; 375 }); 376 }); 377 378 return target; 379} 380 381/** 382 * Returns whether a value is an object of any kind - including DOM nodes, 383 * arrays, regular expressions, etc. Not functions, though. 384 * 385 * This avoids the gotcha where using `typeof` on a `null` value 386 * results in `'object'`. 387 * 388 * @param {Object} value 389 * @return {Boolean} 390 */ 391function isObject(value) { 392 return !!value && (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object'; 393} 394 395/** 396 * Returns whether an object appears to be a "plain" object - that is, a 397 * direct instance of `Object`. 398 * 399 * @param {Object} value 400 * @return {Boolean} 401 */ 402function isPlain(value) { 403 return isObject(value) && toString.call(value) === '[object Object]' && value.constructor === Object; 404} 405 406/** 407 * @file log.js 408 * @module log 409 */ 410var log = void 0; 411 412// This is the private tracking variable for logging level. 413var level = 'info'; 414 415// This is the private tracking variable for the logging history. 416var history = []; 417 418/** 419 * Log messages to the console and history based on the type of message 420 * 421 * @private 422 * @param {string} type 423 * The name of the console method to use. 424 * 425 * @param {Array} args 426 * The arguments to be passed to the matching console method. 427 * 428 * @param {boolean} [stringify] 429 * By default, only old IEs should get console argument stringification, 430 * but this is exposed as a parameter to facilitate testing. 431 */ 432var logByType = function logByType(type, args) { 433 var stringify = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : !!IE_VERSION && IE_VERSION < 11; 434 435 var lvl = log.levels[level]; 436 var lvlRegExp = new RegExp('^(' + lvl + ')$'); 437 438 if (type !== 'log') { 439 440 // Add the type to the front of the message when it's not "log". 441 args.unshift(type.toUpperCase() + ':'); 442 } 443 444 // Add a clone of the args at this point to history. 445 if (history) { 446 history.push([].concat(args)); 447 } 448 449 // Add console prefix after adding to history. 450 args.unshift('VIDEOJS:'); 451 452 // If there's no console then don't try to output messages, but they will 453 // still be stored in history. 454 if (!window_1.console) { 455 return; 456 } 457 458 // Was setting these once outside of this function, but containing them 459 // in the function makes it easier to test cases where console doesn't exist 460 // when the module is executed. 461 var fn = window_1.console[type]; 462 463 if (!fn && type === 'debug') { 464 // Certain browsers don't have support for console.debug. For those, we 465 // should default to the closest comparable log. 466 fn = window_1.console.info || window_1.console.log; 467 } 468 469 // Bail out if there's no console or if this type is not allowed by the 470 // current logging level. 471 if (!fn || !lvl || !lvlRegExp.test(type)) { 472 return; 473 } 474 475 // IEs previous to 11 log objects uselessly as "[object Object]"; so, JSONify 476 // objects and arrays for those less-capable browsers. 477 if (stringify) { 478 args = args.map(function (a) { 479 if (isObject(a) || Array.isArray(a)) { 480 try { 481 return JSON.stringify(a); 482 } catch (x) { 483 return String(a); 484 } 485 } 486 487 // Cast to string before joining, so we get null and undefined explicitly 488 // included in output (as we would in a modern console). 489 return String(a); 490 }).join(' '); 491 } 492 493 // Old IE versions do not allow .apply() for console methods (they are 494 // reported as objects rather than functions). 495 if (!fn.apply) { 496 fn(args); 497 } else { 498 fn[Array.isArray(args) ? 'apply' : 'call'](window_1.console, args); 499 } 500}; 501 502/** 503 * Logs plain debug messages. Similar to `console.log`. 504 * 505 * @class 506 * @param {Mixed[]} args 507 * One or more messages or objects that should be logged. 508 */ 509log = function log() { 510 for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { 511 args[_key] = arguments[_key]; 512 } 513 514 logByType('log', args); 515}; 516 517/** 518 * Enumeration of available logging levels, where the keys are the level names 519 * and the values are `|`-separated strings containing logging methods allowed 520 * in that logging level. These strings are used to create a regular expression 521 * matching the function name being called. 522 * 523 * Levels provided by video.js are: 524 * 525 * - `off`: Matches no calls. Any value that can be cast to `false` will have 526 * this effect. The most restrictive. 527 * - `all`: Matches only Video.js-provided functions (`debug`, `log`, 528 * `log.warn`, and `log.error`). 529 * - `debug`: Matches `log.debug`, `log`, `log.warn`, and `log.error` calls. 530 * - `info` (default): Matches `log`, `log.warn`, and `log.error` calls. 531 * - `warn`: Matches `log.warn` and `log.error` calls. 532 * - `error`: Matches only `log.error` calls. 533 * 534 * @type {Object} 535 */ 536log.levels = { 537 all: 'debug|log|warn|error', 538 off: '', 539 debug: 'debug|log|warn|error', 540 info: 'log|warn|error', 541 warn: 'warn|error', 542 error: 'error', 543 DEFAULT: level 544}; 545 546/** 547 * Get or set the current logging level. If a string matching a key from 548 * {@link log.levels} is provided, acts as a setter. Regardless of argument, 549 * returns the current logging level. 550 * 551 * @param {string} [lvl] 552 * Pass to set a new logging level. 553 * 554 * @return {string} 555 * The current logging level. 556 */ 557log.level = function (lvl) { 558 if (typeof lvl === 'string') { 559 if (!log.levels.hasOwnProperty(lvl)) { 560 throw new Error('"' + lvl + '" in not a valid log level'); 561 } 562 level = lvl; 563 } 564 return level; 565}; 566 567/** 568 * Returns an array containing everything that has been logged to the history. 569 * 570 * This array is a shallow clone of the internal history record. However, its 571 * contents are _not_ cloned; so, mutating objects inside this array will 572 * mutate them in history. 573 * 574 * @return {Array} 575 */ 576log.history = function () { 577 return history ? [].concat(history) : []; 578}; 579 580/** 581 * Clears the internal history tracking, but does not prevent further history 582 * tracking. 583 */ 584log.history.clear = function () { 585 if (history) { 586 history.length = 0; 587 } 588}; 589 590/** 591 * Disable history tracking if it is currently enabled. 592 */ 593log.history.disable = function () { 594 if (history !== null) { 595 history.length = 0; 596 history = null; 597 } 598}; 599 600/** 601 * Enable history tracking if it is currently disabled. 602 */ 603log.history.enable = function () { 604 if (history === null) { 605 history = []; 606 } 607}; 608 609/** 610 * Logs error messages. Similar to `console.error`. 611 * 612 * @param {Mixed[]} args 613 * One or more messages or objects that should be logged as an error 614 */ 615log.error = function () { 616 for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { 617 args[_key2] = arguments[_key2]; 618 } 619 620 return logByType('error', args); 621}; 622 623/**
624 * Logs warning messages. Similar to `console.warn`. 625 * 626 * @param {Mixed[]} args 627 * One or more messages or objects that should be logged as a warning. 628 */ 629log.warn = function () { 630 for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { 631 args[_key3] = arguments[_key3]; 632 } 633 634 return logByType('warn', args); 635}; 636 637/** 638 * Logs debug messages. Similar to `console.debug`, but may also act as a comparable 639 * log if `console.debug` is not available 640 * 641 * @param {Mixed[]} args 642 * One or more messages or objects that should be logged as debug. 643 */ 644log.debug = function () { 645 for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { 646 args[_key4] = arguments[_key4]; 647 } 648 649 return logByType('debug', args); 650}; 651 652var log$1 = log; 653 654function clean (s) { 655 return s.replace(/\n\r?\s*/g, '') 656} 657 658 659var tsml = function tsml (sa) { 660 var s = '' 661 , i = 0; 662 663 for (; i < arguments.length; i++) 664 s += clean(sa[i]) + (arguments[i + 1] || ''); 665 666 return s 667}; 668 669/** 670 * @file computed-style.js 671 * @module computed-style 672 */ 673/** 674 * A safe getComputedStyle with an IE8 fallback. 675 * 676 * This is needed because in Firefox, if the player is loaded in an iframe with 677 * `display:none`, then `getComputedStyle` returns `null`, so, we do a null-check to 678 * make sure that the player doesn't break in these cases. 679 * 680 * @param {Element} el 681 * The element you want the computed style of 682 * 683 * @param {string} prop 684 * The property name you want 685 * 686 * @see https://bugzilla.mozilla.org/show_bug.cgi?id=548397 687 * 688 * @static 689 * @const 690 */ 691function computedStyle(el, prop) { 692 if (!el || !prop) { 693 return ''; 694 } 695 696 if (typeof window_1.getComputedStyle === 'function') { 697 var cs = window_1.getComputedStyle(el); 698 699 return cs ? cs[prop] : ''; 700 } 701 702 return el.currentStyle[prop] || ''; 703} 704 705var _templateObject = taggedTemplateLiteralLoose(['Setting attributes in the second argument of createEl()\n has been deprecated. Use the third argument instead.\n createEl(type, properties, attributes). Attempting to set ', ' to ', '.'], ['Setting attributes in the second argument of createEl()\n has been deprecated. Use the third argument instead.\n createEl(type, properties, attributes). Attempting to set ', ' to ', '.']); 706 707/** 708 * @file dom.js 709 * @module dom 710 */ 711/** 712 * Detect if a value is a string with any non-whitespace characters. 713 * 714 * @param {string} str 715 * The string to check 716 * 717 * @return {boolean} 718 * - True if the string is non-blank 719 * - False otherwise 720 * 721 */ 722function isNonBlankString(str) { 723 return typeof str === 'string' && /\S/.test(str); 724} 725 726/** 727 * Throws an error if the passed string has whitespace. This is used by 728 * class methods to be relatively consistent with the classList API. 729 * 730 * @param {string} str 731 * The string to check for whitespace. 732 * 733 * @throws {Error} 734 * Throws an error if there is whitespace in the string. 735 * 736 */ 737function throwIfWhitespace(str) { 738 if (/\s/.test(str)) { 739 throw new Error('class has illegal whitespace characters'); 740 } 741} 742 743/** 744 * Produce a regular expression for matching a className within an elements className. 745 * 746 * @param {string} className 747 * The className to generate the RegExp for. 748 * 749 * @return {RegExp} 750 * The RegExp that will check for a specific `className` in an elements 751 * className. 752 */ 753function classRegExp(className) { 754 return new RegExp('(^|\\s)' + className + '($|\\s)'); 755} 756 757/** 758 * Whether the current DOM interface appears to be real. 759 * 760 * @return {Boolean} 761 */ 762function isReal() { 763 return ( 764 765 // Both document and window will never be undefined thanks to `global`. 766 document_1 === window_1.document && 767 768 // In IE < 9, DOM methods return "object" as their type, so all we can 769 // confidently check is that it exists. 770 typeof document_1.createElement !== 'undefined' 771 ); 772} 773 774/** 775 * Determines, via duck typing, whether or not a value is a DOM element. 776 * 777 * @param {Mixed} value 778 * The thing to check 779 * 780 * @return {boolean} 781 * - True if it is a DOM element 782 * - False otherwise 783 */ 784function isEl(value) { 785 return isObject(value) && value.nodeType === 1; 786} 787 788/** 789 * Determines if the current DOM is embedded in an iframe. 790 * 791 * @return {boolean} 792 * 793 */ 794function isInFrame() { 795 796 // We need a try/catch here because Safari will throw errors when attempting 797 // to get either `parent` or `self` 798 try { 799 return window_1.parent !== window_1.self; 800 } catch (x) { 801 return true; 802 } 803} 804 805/** 806 * Creates functions to query the DOM using a given method. 807 * 808 * @param {string} method 809 * The method to create the query with. 810 * 811 * @return {Function} 812 * The query method 813 */ 814function createQuerier(method) { 815 return function (selector, context) { 816 if (!isNonBlankString(selector)) { 817 return document_1[method](null); 818 } 819 if (isNonBlankString(context)) { 820 context = document_1.querySelector(context); 821 } 822 823 var ctx = isEl(context) ? context : document_1; 824 825 return ctx[method] && ctx[method](selector); 826 }; 827} 828 829/** 830 * Creates an element and applies properties. 831 * 832 * @param {string} [tagName='div'] 833 * Name of tag to be created. 834 * 835 * @param {Object} [properties={}] 836 * Element properties to be applied. 837 * 838 * @param {Object} [attributes={}] 839 * Element attributes to be applied. 840 * 841 * @param {String|Element|TextNode|Array|Function} [content] 842 * Contents for the element (see: {@link dom:normalizeContent}) 843 * 844 * @return {Element} 845 * The element that was created. 846 */ 847function createEl() { 848 var tagName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'div'; 849 var properties = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 850 var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; 851 var content = arguments[3]; 852 853 var el = document_1.createElement(tagName); 854
vendor: 4,609 bytes, lines 855-1031
855 Object.getOwnPropertyNames(properties).forEach(function (propName) { 856 var val = properties[propName]; 857 858 // See #2176 859 // We originally were accepting both properties and attributes in the 860 // same object, but that doesn't work so well. 861 if (propName.indexOf('aria-') !== -1 || propName === 'role' || propName === 'type') { 862 log$1.warn(tsml(_templateObject, propName, val)); 863 el.setAttribute(propName, val); 864 865 // Handle textContent since it's not supported everywhere and we have a 866 // method for it. 867 } else if (propName === 'textContent') { 868 textContent(el, val); 869 } else { 870 el[propName] = val; 871 } 872 }); 873 874 Object.getOwnPropertyNames(attributes).forEach(function (attrName) { 875 el.setAttribute(attrName, attributes[attrName]); 876 }); 877 878 if (content) { 879 appendContent(el, content); 880 } 881 882 return el; 883} 884 885/** 886 * Injects text into an element, replacing any existing contents entirely. 887 * 888 * @param {Element} el 889 * The element to add text content into 890 * 891 * @param {string} text 892 * The text content to add. 893 * 894 * @return {Element} 895 * The element with added text content. 896 */ 897function textContent(el, text) { 898 if (typeof el.textContent === 'undefined') { 899 el.innerText = text; 900 } else { 901 el.textContent = text; 902 } 903 return el; 904} 905 906/** 907 * Insert an element as the first child node of another 908 * 909 * @param {Element} child 910 * Element to insert 911 * 912 * @param {Element} parent 913 * Element to insert child into 914 */ 915function prependTo(child, parent) { 916 if (parent.firstChild) { 917 parent.insertBefore(child, parent.firstChild); 918 } else { 919 parent.appendChild(child); 920 } 921} 922 923/** 924 * Check if an element has a CSS class 925 * 926 * @param {Element} element 927 * Element to check 928 * 929 * @param {string} classToCheck 930 * Class name to check for 931 * 932 * @return {boolean} 933 * - True if the element had the class 934 * - False otherwise. 935 * 936 * @throws {Error} 937 * Throws an error if `classToCheck` has white space. 938 */ 939function hasClass(element, classToCheck) { 940 throwIfWhitespace(classToCheck); 941 if (element.classList) { 942 return element.classList.contains(classToCheck); 943 } 944 return classRegExp(classToCheck).test(element.className); 945} 946 947/** 948 * Add a CSS class name to an element 949 * 950 * @param {Element} element 951 * Element to add class name to. 952 * 953 * @param {string} classToAdd 954 * Class name to add. 955 * 956 * @return {Element} 957 * The dom element with the added class name. 958 */ 959function addClass(element, classToAdd) { 960 if (element.classList) { 961 element.classList.add(classToAdd); 962 963 // Don't need to `throwIfWhitespace` here because `hasElClass` will do it 964 // in the case of classList not being supported. 965 } else if (!hasClass(element, classToAdd)) { 966 element.className = (element.className + ' ' + classToAdd).trim(); 967 } 968 969 return element; 970} 971 972/** 973 * Remove a CSS class name from an element 974 * 975 * @param {Element} element 976 * Element to remove a class name from. 977 * 978 * @param {string} classToRemove 979 * Class name to remove 980 * 981 * @return {Element} 982 * The dom element with class name removed. 983 */ 984function removeClass(element, classToRemove) { 985 if (element.classList) { 986 element.classList.remove(classToRemove); 987 } else { 988 throwIfWhitespace(classToRemove); 989 element.className = element.className.split(/\s+/).filter(function (c) { 990 return c !== classToRemove; 991 }).join(' '); 992 } 993 994 return element; 995} 996 997/** 998 * The callback definition for toggleElClass. 999 * 1000 * @callback Dom~PredicateCallback 1001 * @param {Element} element 1002 * The DOM element of the Component. 1003 * 1004 * @param {string} classToToggle 1005 * The `className` that wants to be toggled 1006 * 1007 * @return {boolean|undefined} 1008 * - If true the `classToToggle` will get added to `element`. 1009 * - If false the `classToToggle` will get removed from `element`. 1010 * - If undefined this callback will be ignored 1011 */ 1012 1013/** 1014 * Adds or removes a CSS class name on an element depending on an optional 1015 * condition or the presence/absence of the class name. 1016 * 1017 * @param {Element} element 1018 * The element to toggle a class name on. 1019 * 1020 * @param {string} classToToggle 1021 * The class that should be toggled 1022 * 1023 * @param {boolean|PredicateCallback} [predicate] 1024 * See the return value for {@link Dom~PredicateCallback} 1025 * 1026 * @return {Element} 1027 * The element with a class that has been toggled. 1028 */ 1029function toggleClass(element, classToToggle, predicate) { 1030 1031 // This CANNOT use `classList` internally because IE does not support the
vendor: 5,424 bytes, lines 1032-1240
1032 // second parameter to the `classList.toggle()` method! Which is fine because 1033 // `classList` will be used by the add/remove functions. 1034 var has = hasClass(element, classToToggle); 1035 1036 if (typeof predicate === 'function') { 1037 predicate = predicate(element, classToToggle); 1038 } 1039 1040 if (typeof predicate !== 'boolean') { 1041 predicate = !has; 1042 } 1043 1044 // If the necessary class operation matches the current state of the 1045 // element, no action is required. 1046 if (predicate === has) { 1047 return; 1048 } 1049 1050 if (predicate) { 1051 addClass(element, classToToggle); 1052 } else { 1053 removeClass(element, classToToggle); 1054 } 1055 1056 return element; 1057} 1058 1059/** 1060 * Apply attributes to an HTML element. 1061 * 1062 * @param {Element} el 1063 * Element to add attributes to. 1064 * 1065 * @param {Object} [attributes] 1066 * Attributes to be applied. 1067 */ 1068function setAttributes(el, attributes) { 1069 Object.getOwnPropertyNames(attributes).forEach(function (attrName) { 1070 var attrValue = attributes[attrName]; 1071 1072 if (attrValue === null || typeof attrValue === 'undefined' || attrValue === false) { 1073 el.removeAttribute(attrName); 1074 } else { 1075 el.setAttribute(attrName, attrValue === true ? '' : attrValue); 1076 } 1077 }); 1078} 1079 1080/** 1081 * Get an element's attribute values, as defined on the HTML tag 1082 * Attributes are not the same as properties. They're defined on the tag 1083 * or with setAttribute (which shouldn't be used with HTML) 1084 * This will return true or false for boolean attributes. 1085 * 1086 * @param {Element} tag 1087 * Element from which to get tag attributes. 1088 * 1089 * @return {Object} 1090 * All attributes of the element. 1091 */ 1092function getAttributes(tag) { 1093 var obj = {}; 1094 1095 // known boolean attributes 1096 // we can check for matching boolean properties, but older browsers 1097 // won't know about HTML5 boolean attributes that we still read from 1098 var knownBooleans = ',' + 'autoplay,controls,playsinline,loop,muted,default,defaultMuted' + ','; 1099 1100 if (tag && tag.attributes && tag.attributes.length > 0) { 1101 var attrs = tag.attributes; 1102 1103 for (var i = attrs.length - 1; i >= 0; i--) { 1104 var attrName = attrs[i].name; 1105 var attrVal = attrs[i].value; 1106 1107 // check for known booleans 1108 // the matching element property will return a value for typeof 1109 if (typeof tag[attrName] === 'boolean' || knownBooleans.indexOf(',' + attrName + ',') !== -1) { 1110 // the value of an included boolean attribute is typically an empty 1111 // string ('') which would equal false if we just check for a false value. 1112 // we also don't want support bad code like autoplay='false' 1113 attrVal = attrVal !== null ? true : false; 1114 } 1115 1116 obj[attrName] = attrVal; 1117 } 1118 } 1119 1120 return obj; 1121} 1122 1123/** 1124 * Get the value of an element's attribute 1125 * 1126 * @param {Element} el 1127 * A DOM element 1128 * 1129 * @param {string} attribute 1130 * Attribute to get the value of 1131 * 1132 * @return {string} 1133 * value of the attribute 1134 */ 1135function getAttribute(el, attribute) { 1136 return el.getAttribute(attribute); 1137} 1138 1139/** 1140 * Set the value of an element's attribute 1141 * 1142 * @param {Element} el 1143 * A DOM element 1144 * 1145 * @param {string} attribute 1146 * Attribute to set 1147 * 1148 * @param {string} value 1149 * Value to set the attribute to 1150 */ 1151function setAttribute(el, attribute, value) { 1152 el.setAttribute(attribute, value); 1153} 1154 1155/** 1156 * Remove an element's attribute 1157 * 1158 * @param {Element} el 1159 * A DOM element 1160 * 1161 * @param {string} attribute 1162 * Attribute to remove 1163 */ 1164function removeAttribute(el, attribute) { 1165 el.removeAttribute(attribute); 1166} 1167 1168/** 1169 * Attempt to block the ability to select text while dragging controls 1170 */ 1171function blockTextSelection() { 1172 document_1.body.focus(); 1173 document_1.onselectstart = function () { 1174 return false; 1175 }; 1176} 1177 1178/** 1179 * Turn off text selection blocking 1180 */ 1181function unblockTextSelection() { 1182 document_1.onselectstart = function () { 1183 return true; 1184 }; 1185} 1186 1187/** 1188 * Identical to the native `getBoundingClientRect` function, but ensures that 1189 * the method is supported at all (it is in all browsers we claim to support) 1190 * and that the element is in the DOM before continuing. 1191 * 1192 * This wrapper function also shims properties which are not provided by some 1193 * older browsers (namely, IE8). 1194 * 1195 * Additionally, some browsers do not support adding properties to a 1196 * `ClientRect`/`DOMRect` object; so, we shallow-copy it with the standard 1197 * properties (except `x` and `y` which are not widely supported). This helps 1198 * avoid implementations where keys are non-enumerable. 1199 * 1200 * @param {Element} el 1201 * Element whose `ClientRect` we want to calculate. 1202 * 1203 * @return {Object|undefined} 1204 * Always returns a plain 1205 */ 1206function getBoundingClientRect(el) { 1207 if (el && el.getBoundingClientRect && el.parentNode) { 1208 var rect = el.getBoundingClientRect(); 1209 var result = {}; 1210 1211 ['bottom', 'height', 'left', 'right', 'top', 'width'].forEach(function (k) { 1212 if (rect[k] !== undefined) { 1213 result[k] = rect[k]; 1214 } 1215 }); 1216 1217 if (!result.height) { 1218 result.height = parseFloat(computedStyle(el, 'height')); 1219 } 1220 1221 if (!result.width) { 1222 result.width = parseFloat(computedStyle(el, 'width')); 1223 } 1224 1225 return result; 1226 } 1227} 1228 1229/** 1230 * The postion of a DOM element on the page. 1231 * 1232 * @typedef {Object} module:dom~Position 1233 * 1234 * @property {number} left 1235 * Pixels to the left 1236 * 1237 * @property {number} top 1238 * Pixels on top 1239 */ 1240
vendor: 4,060 bytes, lines 1241-1395
1241/** 1242 * Offset Left. 1243 * getBoundingClientRect technique from 1244 * John Resig 1245 * 1246 * @see http://ejohn.org/blog/getboundingclientrect-is-awesome/ 1247 * 1248 * @param {Element} el 1249 * Element from which to get offset 1250 * 1251 * @return {module:dom~Position} 1252 * The position of the element that was passed in. 1253 */ 1254function findPosition(el) { 1255 var box = void 0; 1256 1257 if (el.getBoundingClientRect && el.parentNode) { 1258 box = el.getBoundingClientRect(); 1259 } 1260 1261 if (!box) { 1262 return { 1263 left: 0, 1264 top: 0 1265 }; 1266 } 1267 1268 var docEl = document_1.documentElement; 1269 var body = document_1.body; 1270 1271 var clientLeft = docEl.clientLeft || body.clientLeft || 0; 1272 var scrollLeft = window_1.pageXOffset || body.scrollLeft; 1273 var left = box.left + scrollLeft - clientLeft; 1274 1275 var clientTop = docEl.clientTop || body.clientTop || 0; 1276 var scrollTop = window_1.pageYOffset || body.scrollTop; 1277 var top = box.top + scrollTop - clientTop; 1278 1279 // Android sometimes returns slightly off decimal values, so need to round 1280 return { 1281 left: Math.round(left), 1282 top: Math.round(top) 1283 }; 1284} 1285 1286/** 1287 * x and y coordinates for a dom element or mouse pointer 1288 * 1289 * @typedef {Object} Dom~Coordinates 1290 * 1291 * @property {number} x 1292 * x coordinate in pixels 1293 * 1294 * @property {number} y 1295 * y coordinate in pixels 1296 */ 1297 1298/** 1299 * Get pointer position in element 1300 * Returns an object with x and y coordinates. 1301 * The base on the coordinates are the bottom left of the element. 1302 * 1303 * @param {Element} el 1304 * Element on which to get the pointer position on 1305 * 1306 * @param {EventTarget~Event} event 1307 * Event object 1308 * 1309 * @return {Dom~Coordinates} 1310 * A Coordinates object corresponding to the mouse position. 1311 * 1312 */ 1313function getPointerPosition(el, event) { 1314 var position = {}; 1315 var box = findPosition(el); 1316 var boxW = el.offsetWidth; 1317 var boxH = el.offsetHeight; 1318 1319 var boxY = box.top; 1320 var boxX = box.left; 1321 var pageY = event.pageY; 1322 var pageX = event.pageX; 1323 1324 if (event.changedTouches) { 1325 pageX = event.changedTouches[0].pageX; 1326 pageY = event.changedTouches[0].pageY; 1327 } 1328 1329 position.y = Math.max(0, Math.min(1, (boxY - pageY + boxH) / boxH)); 1330 position.x = Math.max(0, Math.min(1, (pageX - boxX) / boxW)); 1331 1332 return position; 1333} 1334 1335/** 1336 * Determines, via duck typing, whether or not a value is a text node. 1337 * 1338 * @param {Mixed} value 1339 * Check if this value is a text node. 1340 * 1341 * @return {boolean} 1342 * - True if it is a text node 1343 * - False otherwise 1344 */ 1345function isTextNode(value) { 1346 return isObject(value) && value.nodeType === 3; 1347} 1348 1349/** 1350 * Empties the contents of an element. 1351 * 1352 * @param {Element} el 1353 * The element to empty children from 1354 * 1355 * @return {Element} 1356 * The element with no children 1357 */ 1358function emptyEl(el) { 1359 while (el.firstChild) { 1360 el.removeChild(el.firstChild); 1361 } 1362 return el; 1363} 1364 1365/** 1366 * Normalizes content for eventual insertion into the DOM. 1367 * 1368 * This allows a wide range of content definition methods, but protects 1369 * from falling into the trap of simply writing to `innerHTML`, which is 1370 * an XSS concern. 1371 * 1372 * The content for an element can be passed in multiple types and 1373 * combinations, whose behavior is as follows: 1374 * 1375 * @param {String|Element|TextNode|Array|Function} content 1376 * - String: Normalized into a text node. 1377 * - Element/TextNode: Passed through. 1378 * - Array: A one-dimensional array of strings, elements, nodes, or functions 1379 * (which return single strings, elements, or nodes). 1380 * - Function: If the sole argument, is expected to produce a string, element, 1381 * node, or array as defined above. 1382 * 1383 * @return {Array} 1384 * All of the content that was passed in normalized. 1385 */ 1386function normalizeContent(content) { 1387 1388 // First, invoke content if it is a function. If it produces an array, 1389 // that needs to happen before normalization. 1390 if (typeof content === 'function') { 1391 content = content(); 1392 } 1393 1394 // Next up, normalize to an array, so one or many items can be normalized, 1395 // filtered, and returned.
vendor: 4,109 bytes, lines 1396-1536
1396 return (Array.isArray(content) ? content : [content]).map(function (value) { 1397 1398 // First, invoke value if it is a function to produce a new value, 1399 // which will be subsequently normalized to a Node of some kind. 1400 if (typeof value === 'function') { 1401 value = value(); 1402 } 1403 1404 if (isEl(value) || isTextNode(value)) { 1405 return value; 1406 } 1407 1408 if (typeof value === 'string' && /\S/.test(value)) { 1409 return document_1.createTextNode(value); 1410 } 1411 }).filter(function (value) { 1412 return value; 1413 }); 1414} 1415 1416/** 1417 * Normalizes and appends content to an element. 1418 * 1419 * @param {Element} el 1420 * Element to append normalized content to. 1421 * 1422 * 1423 * @param {String|Element|TextNode|Array|Function} content 1424 * See the `content` argument of {@link dom:normalizeContent} 1425 * 1426 * @return {Element} 1427 * The element with appended normalized content. 1428 */ 1429function appendContent(el, content) { 1430 normalizeContent(content).forEach(function (node) { 1431 return el.appendChild(node); 1432 }); 1433 return el; 1434} 1435 1436/** 1437 * Normalizes and inserts content into an element; this is identical to 1438 * `appendContent()`, except it empties the element first. 1439 * 1440 * @param {Element} el 1441 * Element to insert normalized content into. 1442 * 1443 * @param {String|Element|TextNode|Array|Function} content 1444 * See the `content` argument of {@link dom:normalizeContent} 1445 * 1446 * @return {Element} 1447 * The element with inserted normalized content. 1448 * 1449 */ 1450function insertContent(el, content) { 1451 return appendContent(emptyEl(el), content); 1452} 1453 1454/** 1455 * Check if event was a single left click 1456 * 1457 * @param {EventTarget~Event} event 1458 * Event object 1459 * 1460 * @return {boolean} 1461 * - True if a left click 1462 * - False if not a left click 1463 */ 1464function isSingleLeftClick(event) { 1465 // Note: if you create something draggable, be sure to 1466 // call it on both `mousedown` and `mousemove` event, 1467 // otherwise `mousedown` should be enough for a button 1468 1469 if (event.button === undefined && event.buttons === undefined) { 1470 // Why do we need `butttons` ? 1471 // Because, middle mouse sometimes have this: 1472 // e.button === 0 and e.buttons === 4 1473 // Furthermore, we want to prevent combination click, something like 1474 // HOLD middlemouse then left click, that would be 1475 // e.button === 0, e.buttons === 5 1476 // just `button` is not gonna work 1477 1478 // Alright, then what this block does ? 1479 // this is for chrome `simulate mobile devices` 1480 // I want to support this as well 1481 1482 return true; 1483 } 1484 1485 if (event.button === 0 && event.buttons === undefined) { 1486 // Touch screen, sometimes on some specific device, `buttons` 1487 // doesn't have anything (safari on ios, blackberry...) 1488 1489 return true; 1490 } 1491 1492 if (IE_VERSION === 9) { 1493 // Ignore IE9 1494 1495 return true; 1496 } 1497 1498 if (event.button !== 0 || event.buttons !== 1) { 1499 // This is the reason we have those if else block above 1500 // if any special case we can catch and let it slide 1501 // we do it above, when get to here, this definitely 1502 // is-not-left-click 1503 1504 return false; 1505 } 1506 1507 return true; 1508} 1509 1510/** 1511 * Finds a single DOM element matching `selector` within the optional 1512 * `context` of another DOM element (defaulting to `document`). 1513 * 1514 * @param {string} selector 1515 * A valid CSS selector, which will be passed to `querySelector`. 1516 * 1517 * @param {Element|String} [context=document] 1518 * A DOM element within which to query. Can also be a selector 1519 * string in which case the first matching element will be used 1520 * as context. If missing (or no element matches selector), falls 1521 * back to `document`. 1522 * 1523 * @return {Element|null} 1524 * The element that was found or null. 1525 */ 1526var $ = createQuerier('querySelector'); 1527 1528/** 1529 * Finds a all DOM elements matching `selector` within the optional 1530 * `context` of another DOM element (defaulting to `document`). 1531 * 1532 * @param {string} selector 1533 * A valid CSS selector, which will be passed to `querySelectorAll`. 1534 * 1535 * @param {Element|String} [context=document] 1536 * A DOM element within which to query. Can also be a selector
vendor: 5,002 bytes, lines 1537-1751
1537 * string in which case the first matching element will be used 1538 * as context. If missing (or no element matches selector), falls 1539 * back to `document`. 1540 * 1541 * @return {NodeList} 1542 * A element list of elements that were found. Will be empty if none were found. 1543 * 1544 */ 1545var $$ = createQuerier('querySelectorAll'); 1546 1547 1548 1549var Dom = (Object.freeze || Object)({ 1550 isReal: isReal, 1551 isEl: isEl, 1552 isInFrame: isInFrame, 1553 createEl: createEl, 1554 textContent: textContent, 1555 prependTo: prependTo, 1556 hasClass: hasClass, 1557 addClass: addClass, 1558 removeClass: removeClass, 1559 toggleClass: toggleClass, 1560 setAttributes: setAttributes, 1561 getAttributes: getAttributes, 1562 getAttribute: getAttribute, 1563 setAttribute: setAttribute, 1564 removeAttribute: removeAttribute, 1565 blockTextSelection: blockTextSelection, 1566 unblockTextSelection: unblockTextSelection, 1567 getBoundingClientRect: getBoundingClientRect, 1568 findPosition: findPosition, 1569 getPointerPosition: getPointerPosition, 1570 isTextNode: isTextNode, 1571 emptyEl: emptyEl, 1572 normalizeContent: normalizeContent, 1573 appendContent: appendContent, 1574 insertContent: insertContent, 1575 isSingleLeftClick: isSingleLeftClick, 1576 $: $, 1577 $$: $$ 1578}); 1579 1580/** 1581 * @file guid.js 1582 * @module guid 1583 */ 1584 1585/** 1586 * Unique ID for an element or function 1587 * @type {Number} 1588 */ 1589var _guid = 1; 1590 1591/** 1592 * Get a unique auto-incrementing ID by number that has not been returned before. 1593 * 1594 * @return {number} 1595 * A new unique ID. 1596 */ 1597function newGUID() { 1598 return _guid++; 1599} 1600 1601/** 1602 * @file dom-data.js 1603 * @module dom-data 1604 */ 1605/** 1606 * Element Data Store. 1607 * 1608 * Allows for binding data to an element without putting it directly on the 1609 * element. Ex. Event listeners are stored here. 1610 * (also from jsninja.com, slightly modified and updated for closure compiler) 1611 * 1612 * @type {Object} 1613 * @private 1614 */ 1615var elData = {}; 1616 1617/* 1618 * Unique attribute name to store an element's guid in 1619 * 1620 * @type {String} 1621 * @constant 1622 * @private 1623 */ 1624var elIdAttr = 'vdata' + new Date().getTime(); 1625 1626/** 1627 * Returns the cache object where data for an element is stored 1628 * 1629 * @param {Element} el 1630 * Element to store data for. 1631 * 1632 * @return {Object} 1633 * The cache object for that el that was passed in. 1634 */ 1635function getData(el) { 1636 var id = el[elIdAttr]; 1637 1638 if (!id) { 1639 id = el[elIdAttr] = newGUID(); 1640 } 1641 1642 if (!elData[id]) { 1643 elData[id] = {}; 1644 } 1645 1646 return elData[id]; 1647} 1648 1649/** 1650 * Returns whether or not an element has cached data 1651 * 1652 * @param {Element} el 1653 * Check if this element has cached data. 1654 * 1655 * @return {boolean} 1656 * - True if the DOM element has cached data. 1657 * - False otherwise. 1658 */ 1659function hasData(el) { 1660 var id = el[elIdAttr]; 1661 1662 if (!id) { 1663 return false; 1664 } 1665 1666 return !!Object.getOwnPropertyNames(elData[id]).length; 1667} 1668 1669/** 1670 * Delete data for the element from the cache and the guid attr from getElementById 1671 * 1672 * @param {Element} el 1673 * Remove cached data for this element. 1674 */ 1675function removeData(el) { 1676 var id = el[elIdAttr]; 1677 1678 if (!id) { 1679 return; 1680 } 1681 1682 // Remove all stored data 1683 delete elData[id]; 1684 1685 // Remove the elIdAttr property from the DOM node 1686 try { 1687 delete el[elIdAttr]; 1688 } catch (e) { 1689 if (el.removeAttribute) { 1690 el.removeAttribute(elIdAttr); 1691 } else { 1692 // IE doesn't appear to support removeAttribute on the document element 1693 el[elIdAttr] = null; 1694 } 1695 } 1696} 1697 1698/** 1699 * @file events.js. An Event System (John Resig - Secrets of a JS Ninja http://jsninja.com/) 1700 * (Original book version wasn't completely usable, so fixed some things and made Closure Compiler compatible) 1701 * This should work very similarly to jQuery's events, however it's based off the book version which isn't as 1702 * robust as jquery's, so there's probably some differences. 1703 * 1704 * @module events 1705 */ 1706 1707/** 1708 * Clean up the listener cache and dispatchers 1709 * 1710 * @param {Element|Object} elem 1711 * Element to clean up 1712 * 1713 * @param {string} type 1714 * Type of event to clean up 1715 */ 1716function _cleanUpEvents(elem, type) { 1717 var data = getData(elem); 1718 1719 // Remove the events of a particular type if there are none left 1720 if (data.handlers[type].length === 0) { 1721 delete data.handlers[type]; 1722 // data.handlers[type] = null; 1723 // Setting to null was causing an error with data.handlers 1724 1725 // Remove the meta-handler from the element 1726 if (elem.removeEventListener) { 1727 elem.removeEventListener(type, data.dispatcher, false); 1728 } else if (elem.detachEvent) { 1729 elem.detachEvent('on' + type, data.dispatcher); 1730 } 1731 } 1732 1733 // Remove the events object if there are no types left 1734 if (Object.getOwnPropertyNames(data.handlers).length <= 0) { 1735 delete data.handlers; 1736 delete data.dispatcher; 1737 delete data.disabled; 1738 } 1739 1740 // Finally remove the element data if there is no data left 1741 if (Object.getOwnPropertyNames(data).length === 0) { 1742 removeData(elem); 1743 } 1744} 1745 1746/** 1747 * Loops through an array of event types and calls the requested method for each type. 1748 * 1749 * @param {Function} fn 1750 * The event method we want to use. 1751 *
vendor: 4,387 bytes, lines 1752-1886
1752 * @param {Element|Object} elem 1753 * Element or object to bind listeners to 1754 * 1755 * @param {string} type 1756 * Type of event to bind to. 1757 * 1758 * @param {EventTarget~EventListener} callback 1759 * Event listener. 1760 */ 1761function _handleMultipleEvents(fn, elem, types, callback) { 1762 types.forEach(function (type) { 1763 // Call the event method for each one of the types 1764 fn(elem, type, callback); 1765 }); 1766} 1767 1768/** 1769 * Fix a native event to have standard property values 1770 * 1771 * @param {Object} event 1772 * Event object to fix. 1773 * 1774 * @return {Object} 1775 * Fixed event object. 1776 */ 1777function fixEvent(event) { 1778 1779 function returnTrue() { 1780 return true; 1781 } 1782 1783 function returnFalse() { 1784 return false; 1785 } 1786 1787 // Test if fixing up is needed 1788 // Used to check if !event.stopPropagation instead of isPropagationStopped 1789 // But native events return true for stopPropagation, but don't have 1790 // other expected methods like isPropagationStopped. Seems to be a problem 1791 // with the Javascript Ninja code. So we're just overriding all events now. 1792 if (!event || !event.isPropagationStopped) { 1793 var old = event || window_1.event; 1794 1795 event = {}; 1796 // Clone the old object so that we can modify the values event = {}; 1797 // IE8 Doesn't like when you mess with native event properties 1798 // Firefox returns false for event.hasOwnProperty('type') and other props 1799 // which makes copying more difficult. 1800 // TODO: Probably best to create a whitelist of event props 1801 for (var key in old) { 1802 // Safari 6.0.3 warns you if you try to copy deprecated layerX/Y 1803 // Chrome warns you if you try to copy deprecated keyboardEvent.keyLocation 1804 // and webkitMovementX/Y 1805 if (key !== 'layerX' && key !== 'layerY' && key !== 'keyLocation' && key !== 'webkitMovementX' && key !== 'webkitMovementY') { 1806 // Chrome 32+ warns if you try to copy deprecated returnValue, but 1807 // we still want to if preventDefault isn't supported (IE8). 1808 if (!(key === 'returnValue' && old.preventDefault)) { 1809 event[key] = old[key]; 1810 } 1811 } 1812 } 1813 1814 // The event occurred on this element 1815 if (!event.target) { 1816 event.target = event.srcElement || document_1; 1817 } 1818 1819 // Handle which other element the event is related to 1820 if (!event.relatedTarget) { 1821 event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement; 1822 } 1823 1824 // Stop the default browser action 1825 event.preventDefault = function () { 1826 if (old.preventDefault) { 1827 old.preventDefault(); 1828 } 1829 event.returnValue = false; 1830 old.returnValue = false; 1831 event.defaultPrevented = true; 1832 }; 1833 1834 event.defaultPrevented = false; 1835 1836 // Stop the event from bubbling 1837 event.stopPropagation = function () { 1838 if (old.stopPropagation) { 1839 old.stopPropagation(); 1840 } 1841 event.cancelBubble = true; 1842 old.cancelBubble = true; 1843 event.isPropagationStopped = returnTrue; 1844 }; 1845 1846 event.isPropagationStopped = returnFalse; 1847 1848 // Stop the event from bubbling and executing other handlers 1849 event.stopImmediatePropagation = function () { 1850 if (old.stopImmediatePropagation) { 1851 old.stopImmediatePropagation(); 1852 } 1853 event.isImmediatePropagationStopped = returnTrue; 1854 event.stopPropagation(); 1855 }; 1856 1857 event.isImmediatePropagationStopped = returnFalse; 1858 1859 // Handle mouse position 1860 if (event.clientX !== null && event.clientX !== undefined) { 1861 var doc = document_1.documentElement; 1862 var body = document_1.body; 1863 1864 event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); 1865 event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0); 1866 } 1867 1868 // Handle key presses 1869 event.which = event.charCode || event.keyCode; 1870 1871 // Fix button for mouse clicks: 1872 // 0 == left; 1 == middle; 2 == right 1873 if (event.button !== null && event.button !== undefined) { 1874 1875 // The following is disabled because it does not pass videojs-standard 1876 // and... yikes. 1877 /* eslint-disable */ 1878 event.button = event.button & 1 ? 0 : event.button & 4 ? 1 : event.button & 2 ? 2 : 0; 1879 /* eslint-enable */ 1880 } 1881 } 1882 1883 // Returns fixed-up instance 1884 return event; 1885} 1886
vendor: 4,088 bytes, lines 1887-2068
1887/** 1888 * Whether passive event listeners are supported 1889 */ 1890var _supportsPassive = false; 1891 1892(function () { 1893 try { 1894 var opts = Object.defineProperty({}, 'passive', { 1895 get: function get() { 1896 _supportsPassive = true; 1897 } 1898 }); 1899 1900 window_1.addEventListener('test', null, opts); 1901 window_1.removeEventListener('test', null, opts); 1902 } catch (e) { 1903 // disregard 1904 } 1905})(); 1906 1907/** 1908 * Touch events Chrome expects to be passive 1909 */ 1910var passiveEvents = ['touchstart', 'touchmove']; 1911 1912/** 1913 * Add an event listener to element 1914 * It stores the handler function in a separate cache object 1915 * and adds a generic handler to the element's event, 1916 * along with a unique id (guid) to the element. 1917 * 1918 * @param {Element|Object} elem 1919 * Element or object to bind listeners to 1920 * 1921 * @param {string|string[]} type 1922 * Type of event to bind to. 1923 * 1924 * @param {EventTarget~EventListener} fn 1925 * Event listener. 1926 */ 1927function on(elem, type, fn) { 1928 if (Array.isArray(type)) { 1929 return _handleMultipleEvents(on, elem, type, fn); 1930 } 1931 1932 var data = getData(elem); 1933 1934 // We need a place to store all our handler data 1935 if (!data.handlers) { 1936 data.handlers = {}; 1937 } 1938 1939 if (!data.handlers[type]) { 1940 data.handlers[type] = []; 1941 } 1942 1943 if (!fn.guid) { 1944 fn.guid = newGUID(); 1945 } 1946 1947 data.handlers[type].push(fn); 1948 1949 if (!data.dispatcher) { 1950 data.disabled = false; 1951 1952 data.dispatcher = function (event, hash) { 1953 1954 if (data.disabled) { 1955 return; 1956 } 1957 1958 event = fixEvent(event); 1959 1960 var handlers = data.handlers[event.type]; 1961 1962 if (handlers) { 1963 // Copy handlers so if handlers are added/removed during the process it doesn't throw everything off. 1964 var handlersCopy = handlers.slice(0); 1965 1966 for (var m = 0, n = handlersCopy.length; m < n; m++) { 1967 if (event.isImmediatePropagationStopped()) { 1968 break; 1969 } else { 1970 try { 1971 handlersCopy[m].call(elem, event, hash); 1972 } catch (e) { 1973 log$1.error(e); 1974 } 1975 } 1976 } 1977 } 1978 }; 1979 } 1980 1981 if (data.handlers[type].length === 1) { 1982 if (elem.addEventListener) { 1983 var options = false; 1984 1985 if (_supportsPassive && passiveEvents.indexOf(type) > -1) { 1986 options = { passive: true }; 1987 } 1988 elem.addEventListener(type, data.dispatcher, options); 1989 } else if (elem.attachEvent) { 1990 elem.attachEvent('on' + type, data.dispatcher); 1991 } 1992 } 1993} 1994 1995/** 1996 * Removes event listeners from an element 1997 * 1998 * @param {Element|Object} elem 1999 * Object to remove listeners from. 2000 * 2001 * @param {string|string[]} [type] 2002 * Type of listener to remove. Don't include to remove all events from element. 2003 * 2004 * @param {EventTarget~EventListener} [fn] 2005 * Specific listener to remove. Don't include to remove listeners for an event 2006 * type. 2007 */ 2008function off(elem, type, fn) { 2009 // Don't want to add a cache object through getElData if not needed 2010 if (!hasData(elem)) { 2011 return; 2012 } 2013 2014 var data = getData(elem); 2015 2016 // If no events exist, nothing to unbind 2017 if (!data.handlers) { 2018 return; 2019 } 2020 2021 if (Array.isArray(type)) { 2022 return _handleMultipleEvents(off, elem, type, fn); 2023 } 2024 2025 // Utility function 2026 var removeType = function removeType(el, t) { 2027 data.handlers[t] = []; 2028 _cleanUpEvents(el, t); 2029 }; 2030 2031 // Are we removing all bound events? 2032 if (type === undefined) { 2033 for (var t in data.handlers) { 2034 if (Object.prototype.hasOwnProperty.call(data.handlers || {}, t)) { 2035 removeType(elem, t); 2036 } 2037 } 2038 return; 2039 } 2040 2041 var handlers = data.handlers[type]; 2042 2043 // If no handlers exist, nothing to unbind 2044 if (!handlers) { 2045 return; 2046 } 2047 2048 // If no listener was provided, remove all listeners for type 2049 if (!fn) { 2050 removeType(elem, type); 2051 return; 2052 } 2053 2054 // We're only removing a single handler 2055 if (fn.guid) { 2056 for (var n = 0; n < handlers.length; n++) { 2057 if (handlers[n].guid === fn.guid) { 2058 handlers.splice(n--, 1); 2059 } 2060 } 2061 } 2062 2063 _cleanUpEvents(elem, type); 2064} 2065 2066/** 2067 * Trigger an event for an element 2068 *
2069 * @param {Element|Object} elem 2070 * Element to trigger an event on 2071 * 2072 * @param {EventTarget~Event|string} event 2073 * A string (the type) or an event object with a type attribute 2074 * 2075 * @param {Object} [hash] 2076 * data hash to pass along with the event 2077 * 2078 * @return {boolean|undefined} 2079 * - Returns the opposite of `defaultPrevented` if default was prevented 2080 * - Otherwise returns undefined 2081 */ 2082function trigger(elem, event, hash) { 2083 // Fetches element data and a reference to the parent (for bubbling). 2084 // Don't want to add a data object to cache for every parent, 2085 // so checking hasElData first. 2086 var elemData = hasData(elem) ? getData(elem) : {}; 2087 var parent = elem.parentNode || elem.ownerDocument; 2088 // type = event.type || event, 2089 // handler; 2090 2091 // If an event name was passed as a string, creates an event out of it 2092 if (typeof event === 'string') { 2093 event = { type: event, target: elem }; 2094 } 2095 // Normalizes the event properties. 2096 event = fixEvent(event); 2097 2098 // If the passed element has a dispatcher, executes the established handlers. 2099 if (elemData.dispatcher) { 2100 elemData.dispatcher.call(elem, event, hash); 2101 } 2102 2103 // Unless explicitly stopped or the event does not bubble (e.g. media events) 2104 // recursively calls this function to bubble the event up the DOM. 2105 if (parent && !event.isPropagationStopped() && event.bubbles === true) { 2106 trigger.call(null, parent, event, hash); 2107 2108 // If at the top of the DOM, triggers the default action unless disabled. 2109 } else if (!parent && !event.defaultPrevented) { 2110 var targetData = getData(event.target); 2111 2112 // Checks if the target has a default action for this event. 2113 if (event.target[event.type]) { 2114 // Temporarily disables event dispatching on the target as we have already executed the handler. 2115 targetData.disabled = true; 2116 // Executes the default action. 2117 if (typeof event.target[event.type] === 'function') { 2118 event.target[event.type](); 2119 } 2120 // Re-enables event dispatching. 2121 targetData.disabled = false; 2122 } 2123 } 2124 2125 // Inform the triggerer if the default was prevented by returning false 2126 return !event.defaultPrevented; 2127} 2128 2129/** 2130 * Trigger a listener only once for an event 2131 * 2132 * @param {Element|Object} elem 2133 * Element or object to bind to. 2134 * 2135 * @param {string|string[]} type 2136 * Name/type of event 2137 * 2138 * @param {Event~EventListener} fn 2139 * Event Listener function 2140 */ 2141function one(elem, type, fn) { 2142 if (Array.isArray(type)) { 2143 return _handleMultipleEvents(one, elem, type, fn); 2144 } 2145 var func = function func() { 2146 off(elem, type, func); 2147 fn.apply(this, arguments); 2148 }; 2149 2150 // copy the guid to the new function so it can removed using the original function's ID 2151 func.guid = fn.guid = fn.guid || newGUID(); 2152 on(elem, type, func); 2153} 2154 2155var Events = (Object.freeze || Object)({ 2156 fixEvent: fixEvent, 2157 on: on, 2158 off: off, 2159 trigger: trigger, 2160 one: one 2161}); 2162 2163/** 2164 * @file setup.js - Functions for setting up a player without 2165 * user interaction based on the data-setup `attribute` of the video tag. 2166 * 2167 * @module setup 2168 */ 2169var _windowLoaded = false; 2170var videojs$2 = void 0; 2171 2172/** 2173 * Set up any tags that have a data-setup `attribute` when the player is started. 2174 */ 2175var autoSetup = function autoSetup() { 2176 2177 // Protect against breakage in non-browser environments. 2178 if (!isReal()) { 2179 return; 2180 } 2181 2182 // One day, when we stop supporting IE8, go back to this, but in the meantime...*hack hack hack* 2183 // var vids = Array.prototype.slice.call(document.getElementsByTagName('video')); 2184 // var audios = Array.prototype.slice.call(document.getElementsByTagName('audio')); 2185 // var mediaEls = vids.concat(audios); 2186 2187 // Because IE8 doesn't support calling slice on a node list, we need to loop 2188 // through each list of elements to build up a new, combined list of elements. 2189 var vids = document_1.getElementsByTagName('video'); 2190 var audios = document_1.getElementsByTagName('audio'); 2191 var divs = document_1.getElementsByTagName('video-js'); 2192 var mediaEls = []; 2193 2194 if (vids && vids.length > 0) { 2195 for (var i = 0, e = vids.length; i < e; i++) { 2196 mediaEls.push(vids[i]); 2197 } 2198 } 2199 2200 if (audios && audios.length > 0) { 2201 for (var _i = 0, _e = audios.length; _i < _e; _i++) { 2202 mediaEls.push(audios[_i]); 2203 } 2204 } 2205 2206 if (divs && divs.length > 0) { 2207 for (var _i2 = 0, _e2 = divs.length; _i2 < _e2; _i2++) { 2208 mediaEls.push(divs[_i2]); 2209 } 2210 } 2211 2212 // Check if any media elements exist 2213 if (mediaEls && mediaEls.length > 0) { 2214 2215 for (var _i3 = 0, _e3 = mediaEls.length; _i3 < _e3; _i3++) { 2216 var mediaEl = mediaEls[_i3]; 2217 2218 // Check if element exists, has getAttribute func. 2219 // IE seems to consider typeof el.getAttribute == 'object' instead of 2220 // 'function' like expected, at least when loading the player immediately. 2221 if (mediaEl && mediaEl.getAttribute) { 2222 2223 // Make sure this player hasn't already been set up. 2224 if (mediaEl.player === undefined) { 2225 var options = mediaEl.getAttribute('data-setup'); 2226 2227 // Check if data-setup attr exists. 2228 // We only auto-setup if they've added the data-setup attr. 2229 if (options !== null) { 2230 // Create new video.js instance. 2231 videojs$2(mediaEl); 2232 } 2233 } 2234 2235 // If getAttribute isn't defined, we need to wait for the DOM. 2236 } else { 2237 autoSetupTimeout(1); 2238 break; 2239 } 2240 } 2241 2242 // No videos were found, so keep looping unless page is finished loading. 2243 } else if (!_windowLoaded) { 2244 autoSetupTimeout(1); 2245 } 2246}; 2247 2248/** 2249 * Wait until the page is loaded before running autoSetup. This will be called in 2250 * autoSetup if `hasLoaded` returns false. 2251 * 2252 * @param {number} wait 2253 * How long to wait in ms 2254 * 2255 * @param {module:videojs} [vjs] 2256 * The videojs library function 2257 */ 2258function autoSetupTimeout(wait, vjs) { 2259 if (vjs) { 2260 videojs$2 = vjs; 2261 } 2262 2263 window_1.setTimeout(autoSetup, wait); 2264} 2265 2266if (isReal() && document_1.readyState === 'complete') { 2267 _windowLoaded = true; 2268} else { 2269 /** 2270 * Listen for the load event on window, and set _windowLoaded to true. 2271 * 2272 * @listens load 2273 */ 2274 one(window_1, 'load', function () { 2275 _windowLoaded = true; 2276 }); 2277} 2278 2279/** 2280 * @file stylesheet.js 2281 * @module stylesheet 2282 */ 2283/** 2284 * Create a DOM syle element given a className for it. 2285 * 2286 * @param {string} className 2287 * The className to add to the created style element. 2288 * 2289 * @return {Element} 2290 * The element that was created. 2291 */ 2292var createStyleElement = function createStyleElement(className) { 2293 var style = document_1.createElement('style'); 2294 2295 style.className = className; 2296 2297 return style; 2298}; 2299 2300/** 2301 * Add text to a DOM element. 2302 * 2303 * @param {Element} el 2304 * The Element to add text content to. 2305 * 2306 * @param {string} content 2307 * The text to add to the element. 2308 */ 2309var setTextContent = function setTextContent(el, content) { 2310 if (el.styleSheet) { 2311 el.styleSheet.cssText = content; 2312 } else { 2313 el.textContent = content; 2314 } 2315}; 2316 2317/** 2318 * @file fn.js 2319 * @module fn 2320 */ 2321/**
2322 * Bind (a.k.a proxy or Context). A simple method for changing the context of a function 2323 * It also stores a unique id on the function so it can be easily removed from events. 2324 * 2325 * @param {Mixed} context 2326 * The object to bind as scope. 2327 * 2328 * @param {Function} fn 2329 * The function to be bound to a scope. 2330 * 2331 * @param {number} [uid] 2332 * An optional unique ID for the function to be set 2333 * 2334 * @return {Function} 2335 * The new function that will be bound into the context given 2336 */ 2337var bind = function bind(context, fn, uid) { 2338 // Make sure the function has a unique ID 2339 if (!fn.guid) { 2340 fn.guid = newGUID(); 2341 } 2342 2343 // Create the new function that changes the context 2344 var bound = function bound() { 2345 return fn.apply(context, arguments); 2346 }; 2347 2348 // Allow for the ability to individualize this function 2349 // Needed in the case where multiple objects might share the same prototype 2350 // IF both items add an event listener with the same function, then you try to remove just one 2351 // it will remove both because they both have the same guid. 2352 // when using this, you need to use the bind method when you remove the listener as well. 2353 // currently used in text tracks 2354 bound.guid = uid ? uid + '_' + fn.guid : fn.guid; 2355 2356 return bound; 2357}; 2358 2359/** 2360 * Wraps the given function, `fn`, with a new function that only invokes `fn` 2361 * at most once per every `wait` milliseconds. 2362 * 2363 * @param {Function} fn 2364 * The function to be throttled. 2365 * 2366 * @param {Number} wait 2367 * The number of milliseconds by which to throttle. 2368 * 2369 * @return {Function} 2370 */ 2371var throttle = function throttle(fn, wait) { 2372 var last = Date.now(); 2373 2374 var throttled = function throttled() { 2375 var now = Date.now(); 2376 2377 if (now - last >= wait) { 2378 fn.apply(undefined, arguments); 2379 last = now; 2380 } 2381 }; 2382 2383 return throttled; 2384}; 2385 2386/** 2387 * @file src/js/event-target.js 2388 */ 2389/** 2390 * `EventTarget` is a class that can have the same API as the DOM `EventTarget`. It 2391 * adds shorthand functions that wrap around lengthy functions. For example: 2392 * the `on` function is a wrapper around `addEventListener`. 2393 * 2394 * @see [EventTarget Spec]{@link https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget} 2395 * @class EventTarget 2396 */ 2397var EventTarget = function EventTarget() {}; 2398 2399/** 2400 * A Custom DOM event. 2401 * 2402 * @typedef {Object} EventTarget~Event 2403 * @see [Properties]{@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent} 2404 */ 2405 2406/** 2407 * All event listeners should follow the following format. 2408 * 2409 * @callback EventTarget~EventListener 2410 * @this {EventTarget} 2411 * 2412 * @param {EventTarget~Event} event 2413 * the event that triggered this function 2414 * 2415 * @param {Object} [hash] 2416 * hash of data sent during the event 2417 */ 2418 2419/** 2420 * An object containing event names as keys and booleans as values. 2421 * 2422 * > NOTE: If an event name is set to a true value here {@link EventTarget#trigger} 2423 * will have extra functionality. See that function for more information. 2424 * 2425 * @property EventTarget.prototype.allowedEvents_ 2426 * @private 2427 */ 2428EventTarget.prototype.allowedEvents_ = {}; 2429 2430/** 2431 * Adds an `event listener` to an instance of an `EventTarget`. An `event listener` is a 2432 * function that will get called when an event with a certain name gets triggered. 2433 * 2434 * @param {string|string[]} type 2435 * An event name or an array of event names. 2436 * 2437 * @param {EventTarget~EventListener} fn 2438 * The function to call with `EventTarget`s 2439 */ 2440EventTarget.prototype.on = function (type, fn) { 2441 // Remove the addEventListener alias before calling Events.on 2442 // so we don't get into an infinite type loop 2443 var ael = this.addEventListener; 2444 2445 this.addEventListener = function () {}; 2446 on(this, type, fn); 2447 this.addEventListener = ael; 2448}; 2449 2450/** 2451 * An alias of {@link EventTarget#on}. Allows `EventTarget` to mimic 2452 * the standard DOM API. 2453 * 2454 * @function 2455 * @see {@link EventTarget#on} 2456 */ 2457EventTarget.prototype.addEventListener = EventTarget.prototype.on; 2458 2459/** 2460 * Removes an `event listener` for a specific event from an instance of `EventTarget`. 2461 * This makes it so that the `event listener` will no longer get called when the 2462 * named event happens. 2463 * 2464 * @param {string|string[]} type 2465 * An event name or an array of event names. 2466 * 2467 * @param {EventTarget~EventListener} fn 2468 * The function to remove. 2469 */
vendor: 6,590 bytes, lines 2470-2707
2470EventTarget.prototype.off = function (type, fn) { 2471 off(this, type, fn); 2472}; 2473 2474/** 2475 * An alias of {@link EventTarget#off}. Allows `EventTarget` to mimic 2476 * the standard DOM API. 2477 * 2478 * @function 2479 * @see {@link EventTarget#off} 2480 */ 2481EventTarget.prototype.removeEventListener = EventTarget.prototype.off; 2482 2483/** 2484 * This function will add an `event listener` that gets triggered only once. After the 2485 * first trigger it will get removed. This is like adding an `event listener` 2486 * with {@link EventTarget#on} that calls {@link EventTarget#off} on itself. 2487 * 2488 * @param {string|string[]} type 2489 * An event name or an array of event names. 2490 * 2491 * @param {EventTarget~EventListener} fn 2492 * The function to be called once for each event name. 2493 */ 2494EventTarget.prototype.one = function (type, fn) { 2495 // Remove the addEventListener alialing Events.on 2496 // so we don't get into an infinite type loop 2497 var ael = this.addEventListener; 2498 2499 this.addEventListener = function () {}; 2500 one(this, type, fn); 2501 this.addEventListener = ael; 2502}; 2503 2504/** 2505 * This function causes an event to happen. This will then cause any `event listeners` 2506 * that are waiting for that event, to get called. If there are no `event listeners` 2507 * for an event then nothing will happen. 2508 * 2509 * If the name of the `Event` that is being triggered is in `EventTarget.allowedEvents_`. 2510 * Trigger will also call the `on` + `uppercaseEventName` function. 2511 * 2512 * Example: 2513 * 'click' is in `EventTarget.allowedEvents_`, so, trigger will attempt to call 2514 * `onClick` if it exists. 2515 * 2516 * @param {string|EventTarget~Event|Object} event 2517 * The name of the event, an `Event`, or an object with a key of type set to 2518 * an event name. 2519 */ 2520EventTarget.prototype.trigger = function (event) { 2521 var type = event.type || event; 2522 2523 if (typeof event === 'string') { 2524 event = { type: type }; 2525 } 2526 event = fixEvent(event); 2527 2528 if (this.allowedEvents_[type] && this['on' + type]) { 2529 this['on' + type](event); 2530 } 2531 2532 trigger(this, event); 2533}; 2534 2535/** 2536 * An alias of {@link EventTarget#trigger}. Allows `EventTarget` to mimic 2537 * the standard DOM API. 2538 * 2539 * @function 2540 * @see {@link EventTarget#trigger} 2541 */ 2542EventTarget.prototype.dispatchEvent = EventTarget.prototype.trigger; 2543 2544/** 2545 * @file mixins/evented.js 2546 * @module evented 2547 */ 2548/** 2549 * Returns whether or not an object has had the evented mixin applied. 2550 * 2551 * @param {Object} object 2552 * An object to test. 2553 * 2554 * @return {boolean} 2555 * Whether or not the object appears to be evented. 2556 */ 2557var isEvented = function isEvented(object) { 2558 return object instanceof EventTarget || !!object.eventBusEl_ && ['on', 'one', 'off', 'trigger'].every(function (k) { 2559 return typeof object[k] === 'function'; 2560 }); 2561}; 2562 2563/** 2564 * Whether a value is a valid event type - non-empty string or array. 2565 * 2566 * @private 2567 * @param {string|Array} type 2568 * The type value to test. 2569 * 2570 * @return {boolean} 2571 * Whether or not the type is a valid event type. 2572 */ 2573var isValidEventType = function isValidEventType(type) { 2574 return ( 2575 // The regex here verifies that the `type` contains at least one non- 2576 // whitespace character. 2577 typeof type === 'string' && /\S/.test(type) || Array.isArray(type) && !!type.length 2578 ); 2579}; 2580 2581/** 2582 * Validates a value to determine if it is a valid event target. Throws if not. 2583 * 2584 * @private 2585 * @throws {Error} 2586 * If the target does not appear to be a valid event target. 2587 * 2588 * @param {Object} target 2589 * The object to test. 2590 */ 2591var validateTarget = function validateTarget(target) { 2592 if (!target.nodeName && !isEvented(target)) { 2593 throw new Error('Invalid target; must be a DOM node or evented object.'); 2594 } 2595}; 2596 2597/** 2598 * Validates a value to determine if it is a valid event target. Throws if not. 2599 * 2600 * @private 2601 * @throws {Error} 2602 * If the type does not appear to be a valid event type. 2603 * 2604 * @param {string|Array} type 2605 * The type to test. 2606 */ 2607var validateEventType = function validateEventType(type) { 2608 if (!isValidEventType(type)) { 2609 throw new Error('Invalid event type; must be a non-empty string or array.'); 2610 } 2611}; 2612 2613/** 2614 * Validates a value to determine if it is a valid listener. Throws if not. 2615 * 2616 * @private 2617 * @throws {Error} 2618 * If the listener is not a function. 2619 * 2620 * @param {Function} listener 2621 * The listener to test. 2622 */ 2623var validateListener = function validateListener(listener) { 2624 if (typeof listener !== 'function') { 2625 throw new Error('Invalid listener; must be a function.'); 2626 } 2627}; 2628 2629/** 2630 * Takes an array of arguments given to `on()` or `one()`, validates them, and 2631 * normalizes them into an object. 2632 * 2633 * @private 2634 * @param {Object} self 2635 * The evented object on which `on()` or `one()` was called. This 2636 * object will be bound as the `this` value for the listener. 2637 * 2638 * @param {Array} args 2639 * An array of arguments passed to `on()` or `one()`. 2640 * 2641 * @return {Object} 2642 * An object containing useful values for `on()` or `one()` calls. 2643 */ 2644var normalizeListenArgs = function normalizeListenArgs(self, args) { 2645 2646 // If the number of arguments is less than 3, the target is always the 2647 // evented object itself. 2648 var isTargetingSelf = args.length < 3 || args[0] === self || args[0] === self.eventBusEl_; 2649 var target = void 0; 2650 var type = void 0; 2651 var listener = void 0; 2652 2653 if (isTargetingSelf) { 2654 target = self.eventBusEl_; 2655 2656 // Deal with cases where we got 3 arguments, but we are still listening to 2657 // the evented object itself. 2658 if (args.length >= 3) { 2659 args.shift(); 2660 } 2661 2662 type = args[0]; 2663 listener = args[1]; 2664 } else { 2665 target = args[0]; 2666 type = args[1]; 2667 listener = args[2]; 2668 } 2669 2670 validateTarget(target); 2671 validateEventType(type); 2672 validateListener(listener); 2673 2674 listener = bind(self, listener); 2675 2676 return { isTargetingSelf: isTargetingSelf, target: target, type: type, listener: listener }; 2677}; 2678 2679/** 2680 * Adds the listener to the event type(s) on the target, normalizing for 2681 * the type of target. 2682 * 2683 * @private 2684 * @param {Element|Object} target 2685 * A DOM node or evented object. 2686 * 2687 * @param {string} method 2688 * The event binding method to use ("on" or "one"). 2689 * 2690 * @param {string|Array} type 2691 * One or more event type(s). 2692 * 2693 * @param {Function} listener 2694 * A listener function. 2695 */ 2696var listen = function listen(target, method, type, listener) { 2697 validateTarget(target); 2698 2699 if (target.nodeName) { 2700 Events[method](target, type, listener); 2701 } else { 2702 target[method](type, listener); 2703 } 2704}; 2705 2706/** 2707 * Contains methods that provide event capabilit
vendor: 8,396 bytes, lines 2707-2949
2707es to an object which is passed 2708 * to {@link module:evented|evented}. 2709 * 2710 * @mixin EventedMixin 2711 */ 2712var EventedMixin = { 2713 2714 /** 2715 * Add a listener to an event (or events) on this object or another evented 2716 * object. 2717 * 2718 * @param {string|Array|Element|Object} targetOrType 2719 * If this is a string or array, it represents the event type(s) 2720 * that will trigger the listener. 2721 * 2722 * Another evented object can be passed here instead, which will 2723 * cause the listener to listen for events on _that_ object. 2724 * 2725 * In either case, the listener's `this` value will be bound to 2726 * this object. 2727 * 2728 * @param {string|Array|Function} typeOrListener 2729 * If the first argument was a string or array, this should be the 2730 * listener function. Otherwise, this is a string or array of event 2731 * type(s). 2732 * 2733 * @param {Function} [listener] 2734 * If the first argument was another evented object, this will be 2735 * the listener function. 2736 */ 2737 on: function on$$1() { 2738 var _this = this; 2739 2740 for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { 2741 args[_key] = arguments[_key]; 2742 } 2743 2744 var _normalizeListenArgs = normalizeListenArgs(this, args), 2745 isTargetingSelf = _normalizeListenArgs.isTargetingSelf, 2746 target = _normalizeListenArgs.target, 2747 type = _normalizeListenArgs.type, 2748 listener = _normalizeListenArgs.listener; 2749 2750 listen(target, 'on', type, listener); 2751 2752 // If this object is listening to another evented object. 2753 if (!isTargetingSelf) { 2754 2755 // If this object is disposed, remove the listener. 2756 var removeListenerOnDispose = function removeListenerOnDispose() { 2757 return _this.off(target, type, listener); 2758 }; 2759 2760 // Use the same function ID as the listener so we can remove it later it 2761 // using the ID of the original listener. 2762 removeListenerOnDispose.guid = listener.guid; 2763 2764 // Add a listener to the target's dispose event as well. This ensures 2765 // that if the target is disposed BEFORE this object, we remove the 2766 // removal listener that was just added. Otherwise, we create a memory leak. 2767 var removeRemoverOnTargetDispose = function removeRemoverOnTargetDispose() { 2768 return _this.off('dispose', removeListenerOnDispose); 2769 }; 2770 2771 // Use the same function ID as the listener so we can remove it later 2772 // it using the ID of the original listener. 2773 removeRemoverOnTargetDispose.guid = listener.guid; 2774 2775 listen(this, 'on', 'dispose', removeListenerOnDispose); 2776 listen(target, 'on', 'dispose', removeRemoverOnTargetDispose); 2777 } 2778 }, 2779 2780 2781 /** 2782 * Add a listener to an event (or events) on this object or another evented 2783 * object. The listener will only be called once and then removed. 2784 * 2785 * @param {string|Array|Element|Object} targetOrType 2786 * If this is a string or array, it represents the event type(s) 2787 * that will trigger the listener. 2788 * 2789 * Another evented object can be passed here instead, which will 2790 * cause the listener to listen for events on _that_ object. 2791 * 2792 * In either case, the listener's `this` value will be bound to 2793 * this object. 2794 * 2795 * @param {string|Array|Function} typeOrListener 2796 * If the first argument was a string or array, this should be the 2797 * listener function. Otherwise, this is a string or array of event 2798 * type(s). 2799 * 2800 * @param {Function} [listener] 2801 * If the first argument was another evented object, this will be 2802 * the listener function. 2803 */ 2804 one: function one$$1() { 2805 var _this2 = this; 2806 2807 for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { 2808 args[_key2] = arguments[_key2]; 2809 } 2810 2811 var _normalizeListenArgs2 = normalizeListenArgs(this, args), 2812 isTargetingSelf = _normalizeListenArgs2.isTargetingSelf, 2813 target = _normalizeListenArgs2.target, 2814 type = _normalizeListenArgs2.type, 2815 listener = _normalizeListenArgs2.listener; 2816 2817 // Targeting this evented object. 2818 2819 2820 if (isTargetingSelf) { 2821 listen(target, 'one', type, listener); 2822 2823 // Targeting another evented object. 2824 } else { 2825 var wrapper = function wrapper() { 2826 for (var _len3 = arguments.length, largs = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { 2827 largs[_key3] = arguments[_key3]; 2828 } 2829 2830 _this2.off(target, type, wrapper); 2831 listener.apply(null, largs); 2832 }; 2833 2834 // Use the same function ID as the listener so we can remove it later 2835 // it using the ID of the original listener. 2836 wrapper.guid = listener.guid; 2837 listen(target, 'one', type, wrapper); 2838 } 2839 }, 2840 2841 2842 /** 2843 * Removes listener(s) from event(s) on an evented object. 2844 * 2845 * @param {string|Array|Element|Object} [targetOrType] 2846 * If this is a string or array, it represents the event type(s). 2847 * 2848 * Another evented object can be passed here instead, in which case 2849 * ALL 3 arguments are _required_. 2850 * 2851 * @param {string|Array|Function} [typeOrListener] 2852 * If the first argument was a string or array, this may be the 2853 * listener function. Otherwise, this is a string or array of event 2854 * type(s). 2855 * 2856 * @param {Function} [listener] 2857 * If the first argument was another evented object, this will be 2858 * the listener function; otherwise, _all_ listeners bound to the 2859 * event type(s) will be removed. 2860 */ 2861 off: function off$$1(targetOrType, typeOrListener, listener) { 2862 2863 // Targeting this evented object. 2864 if (!targetOrType || isValidEventType(targetOrType)) { 2865 off(this.eventBusEl_, targetOrType, typeOrListener); 2866 2867 // Targeting another evented object. 2868 } else { 2869 var target = targetOrType; 2870 var type = typeOrListener; 2871 2872 // Fail fast and in a meaningful way! 2873 validateTarget(target); 2874 validateEventType(type); 2875 validateListener(listener); 2876 2877 // Ensure there's at least a guid, even if the function hasn't been used 2878 listener = bind(this, listener); 2879 2880 // Remove the dispose listener on this evented object, which was given 2881 // the same guid as the event listener in on(). 2882 this.off('dispose', listener); 2883 2884 if (target.nodeName) { 2885 off(target, type, listener); 2886 off(target, 'dispose', listener); 2887 } else if (isEvented(target)) { 2888 target.off(type, listener); 2889 target.off('dispose', listener); 2890 } 2891 } 2892 }, 2893 2894 2895 /** 2896 * Fire an event on this evented object, causing its listeners to be called. 2897 * 2898 * @param {string|Object} event 2899 * An event type or an object with a type property. 2900 * 2901 * @param {Object} [hash] 2902 * An additional object to pass along to listeners. 2903 * 2904 * @returns {boolean} 2905 * Whether or not the default behavior was prevented. 2906 */ 2907 trigger: function trigger$$1(event, hash) { 2908 return trigger(this.eventBusEl_, event, hash); 2909 } 2910}; 2911 2912/** 2913 * Applies {@link module:evented~EventedMixin|EventedMixin} to a target object. 2914 * 2915 * @param {Object} target 2916 * The object to which to add event methods. 2917 * 2918 * @param {Object} [options={}] 2919 * Options for customizing the mixin behavior. 2920 * 2921 * @param {String} [options.eventBusKey] 2922 * By default, adds a `eventBusEl_` DOM element to the target object, 2923 * which is used as an event bus. If the target object already has a 2924 * DOM element that should be used, pass its key here. 2925 * 2926 * @return {Object} 2927 * The target object. 2928 */ 2929function evented(target) { 2930 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 2931 var eventBusKey = options.eventBusKey; 2932 2933 // Set or create the eventBusEl_. 2934 2935 if (eventBusKey) { 2936 if (!target[eventBusKey].nodeName) { 2937 throw new Error('The eventBusKey "' + eventBusKey + '" does not refer to an element.'); 2938 } 2939 target.eventBusEl_ = target[eventBusKey]; 2940 } else { 2941 target.eventBusEl_ = createEl('span', { className: 'vjs-event-bus' }); 2942 } 2943 2944 assign(target, EventedMixin); 2945 2946 // When any evented object is disposed, it removes all its listeners. 2947 target.on('dispose', function () { 2948 target.off(); 2949
vendor: 9,527 bytes, lines 2949-3308
2949window_1.setTimeout(function () { 2950 target.eventBusEl_ = null; 2951 }, 0); 2952 }); 2953 2954 return target; 2955} 2956 2957/** 2958 * @file mixins/stateful.js 2959 * @module stateful 2960 */ 2961/** 2962 * Contains methods that provide statefulness to an object which is passed 2963 * to {@link module:stateful}. 2964 * 2965 * @mixin StatefulMixin 2966 */ 2967var StatefulMixin = { 2968 2969 /** 2970 * A hash containing arbitrary keys and values representing the state of 2971 * the object. 2972 * 2973 * @type {Object} 2974 */ 2975 state: {}, 2976 2977 /** 2978 * Set the state of an object by mutating its 2979 * {@link module:stateful~StatefulMixin.state|state} object in place. 2980 * 2981 * @fires module:stateful~StatefulMixin#statechanged 2982 * @param {Object|Function} stateUpdates 2983 * A new set of properties to shallow-merge into the plugin state. 2984 * Can be a plain object or a function returning a plain object. 2985 * 2986 * @returns {Object|undefined} 2987 * An object containing changes that occurred. If no changes 2988 * occurred, returns `undefined`. 2989 */ 2990 setState: function setState(stateUpdates) { 2991 var _this = this; 2992 2993 // Support providing the `stateUpdates` state as a function. 2994 if (typeof stateUpdates === 'function') { 2995 stateUpdates = stateUpdates(); 2996 } 2997 2998 var changes = void 0; 2999 3000 each(stateUpdates, function (value, key) { 3001 3002 // Record the change if the value is different from what's in the 3003 // current state. 3004 if (_this.state[key] !== value) { 3005 changes = changes || {}; 3006 changes[key] = { 3007 from: _this.state[key], 3008 to: value 3009 }; 3010 } 3011 3012 _this.state[key] = value; 3013 }); 3014 3015 // Only trigger "statechange" if there were changes AND we have a trigger 3016 // function. This allows us to not require that the target object be an 3017 // evented object. 3018 if (changes && isEvented(this)) { 3019 3020 /** 3021 * An event triggered on an object that is both 3022 * {@link module:stateful|stateful} and {@link module:evented|evented} 3023 * indicating that its state has changed. 3024 * 3025 * @event module:stateful~StatefulMixin#statechanged 3026 * @type {Object} 3027 * @property {Object} changes 3028 * A hash containing the properties that were changed and 3029 * the values they were changed `from` and `to`. 3030 */ 3031 this.trigger({ 3032 changes: changes, 3033 type: 'statechanged' 3034 }); 3035 } 3036 3037 return changes; 3038 } 3039}; 3040 3041/** 3042 * Applies {@link module:stateful~StatefulMixin|StatefulMixin} to a target 3043 * object. 3044 * 3045 * If the target object is {@link module:evented|evented} and has a 3046 * `handleStateChanged` method, that method will be automatically bound to the 3047 * `statechanged` event on itself. 3048 * 3049 * @param {Object} target 3050 * The object to be made stateful. 3051 * 3052 * @param {Object} [defaultState] 3053 * A default set of properties to populate the newly-stateful object's 3054 * `state` property. 3055 * 3056 * @returns {Object} 3057 * Returns the `target`. 3058 */ 3059function stateful(target, defaultState) { 3060 assign(target, StatefulMixin); 3061 3062 // This happens after the mixing-in because we need to replace the `state` 3063 // added in that step. 3064 target.state = assign({}, target.state, defaultState); 3065 3066 // Auto-bind the `handleStateChanged` method of the target object if it exists. 3067 if (typeof target.handleStateChanged === 'function' && isEvented(target)) { 3068 target.on('statechanged', target.handleStateChanged); 3069 } 3070 3071 return target; 3072} 3073 3074/** 3075 * @file to-title-case.js 3076 * @module to-title-case 3077 */ 3078 3079/** 3080 * Uppercase the first letter of a string. 3081 * 3082 * @param {string} string 3083 * String to be uppercased 3084 * 3085 * @return {string} 3086 * The string with an uppercased first letter 3087 */ 3088function toTitleCase(string) { 3089 if (typeof string !== 'string') { 3090 return string; 3091 } 3092 3093 return string.charAt(0).toUpperCase() + string.slice(1); 3094} 3095 3096/** 3097 * Compares the TitleCase versions of the two strings for equality. 3098 * 3099 * @param {string} str1 3100 * The first string to compare 3101 * 3102 * @param {string} str2 3103 * The second string to compare 3104 * 3105 * @return {boolean} 3106 * Whether the TitleCase versions of the strings are equal 3107 */ 3108function titleCaseEquals(str1, str2) { 3109 return toTitleCase(str1) === toTitleCase(str2); 3110} 3111 3112/** 3113 * @file merge-options.js 3114 * @module merge-options 3115 */ 3116/** 3117 * Deep-merge one or more options objects, recursively merging **only** plain 3118 * object properties. 3119 * 3120 * @param {Object[]} sources 3121 * One or more objects to merge into a new object. 3122 * 3123 * @returns {Object} 3124 * A new object that is the merged result of all sources. 3125 */ 3126function mergeOptions() { 3127 var result = {}; 3128 3129 for (var _len = arguments.length, sources = Array(_len), _key = 0; _key < _len; _key++) { 3130 sources[_key] = arguments[_key]; 3131 } 3132 3133 sources.forEach(function (source) { 3134 if (!source) { 3135 return; 3136 } 3137 3138 each(source, function (value, key) { 3139 if (!isPlain(value)) { 3140 result[key] = value; 3141 return; 3142 } 3143 3144 if (!isPlain(result[key])) { 3145 result[key] = {}; 3146 } 3147 3148 result[key] = mergeOptions(result[key], value); 3149 }); 3150 }); 3151 3152 return result; 3153} 3154 3155/** 3156 * Player Component - Base class for all UI objects 3157 * 3158 * @file component.js 3159 */ 3160/** 3161 * Base class for all UI Components. 3162 * Components are UI objects which represent both a javascript object and an element 3163 * in the DOM. They can be children of other components, and can have 3164 * children themselves. 3165 * 3166 * Components can also use methods from {@link EventTarget} 3167 */ 3168 3169var Component = function () { 3170 3171 /** 3172 * A callback that is called when a component is ready. Does not have any 3173 * paramters and any callback value will be ignored. 3174 * 3175 * @callback Component~ReadyCallback 3176 * @this Component 3177 */ 3178 3179 /** 3180 * Creates an instance of this class. 3181 * 3182 * @param {Player} player 3183 * The `Player` that this class should be attached to. 3184 * 3185 * @param {Object} [options] 3186 * The key/value store of player options. 3187 * 3188 * @param {Object[]} [options.children] 3189 * An array of children objects to intialize this component with. Children objects have 3190 * a name property that will be used if more than one component of the same type needs to be 3191 * added. 3192 * 3193 * @param {Component~ReadyCallback} [ready] 3194 * Function that gets called when the `Component` is ready. 3195 */ 3196 function Component(player, options, ready) { 3197 classCallCheck(this, Component); 3198 3199 3200 // The component might be the player itself and we can't pass `this` to super 3201 if (!player && this.play) { 3202 this.player_ = player = this; // eslint-disable-line 3203 } else { 3204 this.player_ = player; 3205 } 3206 3207 // Make a copy of prototype.options_ to protect against overriding defaults 3208 this.options_ = mergeOptions({}, this.options_); 3209 3210 // Updated options with supplied options 3211 options = this.options_ = mergeOptions(this.options_, options); 3212 3213 // Get ID from options or options element if one is supplied 3214 this.id_ = options.id || options.el && options.el.id; 3215 3216 // If there was no ID from the options, generate one 3217 if (!this.id_) { 3218 // Don't require the player ID function in the case of mock players 3219 var id = player && player.id && player.id() || 'no_player'; 3220 3221 this.id_ = id + '_component_' + newGUID(); 3222 } 3223 3224 this.name_ = options.name || null; 3225 3226 // Create element if one wasn't provided in options 3227 if (options.el) { 3228 this.el_ = options.el; 3229 } else if (options.createEl !== false) { 3230 this.el_ = this.createEl(); 3231 } 3232 3233 // if evented is anything except false, we want to mixin in evented 3234 if (options.evented !== false) { 3235 // Make this an evented object and use `el_`, if available, as its event bus 3236 evented(this, { eventBusKey: this.el_ ? 'el_' : null }); 3237 } 3238 stateful(this, this.constructor.defaultState); 3239 3240 this.children_ = []; 3241 this.childIndex_ = {}; 3242 this.childNameIndex_ = {}; 3243 3244 // Add any child components in options 3245 if (options.initChildren !== false) { 3246 this.initChildren(); 3247 } 3248 3249 this.ready(ready); 3250 // Don't want to trigger ready here or it will before init is actually 3251 // finished for all children that run this constructor 3252 3253 if (options.reportTouchActivity !== false) { 3254 this.enableTouchActivity(); 3255 } 3256 } 3257 3258 /** 3259 * Dispose of the `Component` and all child components. 3260 * 3261 * @fires Component#dispose 3262 */ 3263 3264 3265 Component.prototype.dispose = function dispose() { 3266 3267 /** 3268 * Triggered when a `Component` is disposed. 3269 * 3270 * @event Component#dispose 3271 * @type {EventTarget~Event} 3272 * 3273 * @property {boolean} [bubbles=false] 3274 * set to false so that the close event does not 3275 * bubble up 3276 */ 3277 this.trigger({ type: 'dispose', bubbles: false }); 3278 3279 // Dispose all children. 3280 if (this.children_) { 3281 for (var i = this.children_.length - 1; i >= 0; i--) { 3282 if (this.children_[i].dispose) { 3283 this.children_[i].dispose(); 3284 } 3285 } 3286 } 3287 3288 // Delete child references 3289 this.children_ = null; 3290 this.childIndex_ = null; 3291 this.childNameIndex_ = null; 3292 3293 if (this.el_) { 3294 // Remove element from DOM 3295 if (this.el_.parentNode) { 3296 this.el_.parentNode.removeChild(this.el_); 3297 } 3298 3299 removeData(this.el_); 3300 this.el_ = null; 3301 } 3302 3303 // remove reference to the player after disposing of the element 3304 this.player_ = null; 3305 }; 3306 3307 /** 3308 * Return the {@link Player}
3308 that the `Component` has attached to. 3309 * 3310 * @return {Player} 3311 * The player that this `Component` has attached to. 3312 */ 3313 3314 3315 Component.prototype.player = function player() { 3316 return this.player_; 3317 }; 3318 3319 /** 3320 * Deep merge of options objects with new options. 3321 * > Note: When both `obj` and `options` contain properties whose values are objects. 3322 * The two properties get merged using {@link module:mergeOptions} 3323 * 3324 * @param {Object} obj 3325 * The object that contains new options. 3326 * 3327 * @return {Object} 3328 * A new object of `this.options_` and `obj` merged together. 3329 * 3330 * @deprecated since version 5 3331 */ 3332 3333 3334 Component.prototype.options = function options(obj) { 3335 log$1.warn('this.options() has been deprecated and will be moved to the constructor in 6.0'); 3336 3337 if (!obj) { 3338 return this.options_; 3339 } 3340 3341 this.options_ = mergeOptions(this.options_, obj); 3342 return this.options_; 3343 }; 3344 3345 /** 3346 * Get the `Component`s DOM element 3347 * 3348 * @return {Element} 3349 * The DOM element for this `Component`. 3350 */ 3351 3352 3353 Component.prototype.el = function el() { 3354 return this.el_; 3355 }; 3356 3357 /** 3358 * Create the `Component`s DOM element. 3359 * 3360 * @param {string} [tagName] 3361 * Element's DOM node type. e.g. 'div' 3362 * 3363 * @param {Object} [properties] 3364 * An object of properties that should be set. 3365 * 3366 * @param {Object} [attributes] 3367 * An object of attributes that should be set. 3368 * 3369 * @return {Element} 3370 * The element that gets created. 3371 */ 3372 3373 3374 Component.prototype.createEl = function createEl$$1(tagName, properties, attributes) { 3375 return createEl(tagName, properties, attributes); 3376 }; 3377 3378 /** 3379 * Localize a string given the string in english. 3380 * 3381 * If tokens are provided, it'll try and run a simple token replacement on the provided string. 3382 * The tokens it lo
vendor: 24,326 bytes, lines 3382-4202
3382ooks for look like `{1}` with the index being 1-indexed into the tokens array. 3383 * 3384 * If a `defaultValue` is provided, it'll use that over `string`, 3385 * if a value isn't found in provided language files. 3386 * This is useful if you want to have a descriptive key for token replacement 3387 * but have a succinct localized string and not require `en.json` to be included. 3388 * 3389 * Currently, it is used for the progress bar timing. 3390 * ```js 3391 * { 3392 * "progress bar timing: currentTime={1} duration={2}": "{1} of {2}" 3393 * } 3394 * ``` 3395 * It is then used like so: 3396 * ```js 3397 * this.localize('progress bar timing: currentTime={1} duration{2}', 3398 * [this.player_.currentTime(), this.player_.duration()], 3399 * '{1} of {2}'); 3400 * ``` 3401 * 3402 * Which outputs something like: `01:23 of 24:56`. 3403 * 3404 * 3405 * @param {string} string 3406 * The string to localize and the key to lookup in the language files. 3407 * @param {string[]} [tokens] 3408 * If the current item has token replacements, provide the tokens here. 3409 * @param {string} [defaultValue] 3410 * Defaults to `string`. Can be a default value to use for token replacement 3411 * if the lookup key is needed to be separate. 3412 * 3413 * @return {string} 3414 * The localized string or if no localization exists the english string. 3415 */ 3416 3417 3418 Component.prototype.localize = function localize(string, tokens) { 3419 var defaultValue = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : string; 3420 3421 var code = this.player_.language && this.player_.language(); 3422 var languages = this.player_.languages && this.player_.languages(); 3423 var language = languages && languages[code]; 3424 var primaryCode = code && code.split('-')[0]; 3425 var primaryLang = languages && languages[primaryCode]; 3426 3427 var localizedString = defaultValue; 3428 3429 if (language && language[string]) { 3430 localizedString = language[string]; 3431 } else if (primaryLang && primaryLang[string]) { 3432 localizedString = primaryLang[string]; 3433 } 3434 3435 if (tokens) { 3436 localizedString = localizedString.replace(/\{(\d+)\}/g, function (match, index) { 3437 var value = tokens[index - 1]; 3438 var ret = value; 3439 3440 if (typeof value === 'undefined') { 3441 ret = match; 3442 } 3443 3444 return ret; 3445 }); 3446 } 3447 3448 return localizedString; 3449 }; 3450 3451 /** 3452 * Return the `Component`s DOM element. This is where children get inserted. 3453 * This will usually be the the same as the element returned in {@link Component#el}. 3454 * 3455 * @return {Element} 3456 * The content element for this `Component`. 3457 */ 3458 3459 3460 Component.prototype.contentEl = function contentEl() { 3461 return this.contentEl_ || this.el_; 3462 }; 3463 3464 /** 3465 * Get this `Component`s ID 3466 * 3467 * @return {string} 3468 * The id of this `Component` 3469 */ 3470 3471 3472 Component.prototype.id = function id() { 3473 return this.id_; 3474 }; 3475 3476 /** 3477 * Get the `Component`s name. The name gets used to reference the `Component` 3478 * and is set during registration. 3479 * 3480 * @return {string} 3481 * The name of this `Component`. 3482 */ 3483 3484 3485 Component.prototype.name = function name() { 3486 return this.name_; 3487 }; 3488 3489 /** 3490 * Get an array of all child components 3491 * 3492 * @return {Array} 3493 * The children 3494 */ 3495 3496 3497 Component.prototype.children = function children() { 3498 return this.children_; 3499 }; 3500 3501 /** 3502 * Returns the child `Component` with the given `id`. 3503 * 3504 * @param {string} id 3505 * The id of the child `Component` to get. 3506 * 3507 * @return {Component|undefined} 3508 * The child `Component` with the given `id` or undefined. 3509 */ 3510 3511 3512 Component.prototype.getChildById = function getChildById(id) { 3513 return this.childIndex_[id]; 3514 }; 3515 3516 /** 3517 * Returns the child `Component` with the given `name`. 3518 * 3519 * @param {string} name 3520 * The name of the child `Component` to get. 3521 * 3522 * @return {Component|undefined} 3523 * The child `Component` with the given `name` or undefined. 3524 */ 3525 3526 3527 Component.prototype.getChild = function getChild(name) { 3528 if (!name) { 3529 return; 3530 } 3531 3532 name = toTitleCase(name); 3533 3534 return this.childNameIndex_[name]; 3535 }; 3536 3537 /** 3538 * Add a child `Component` inside the current `Component`. 3539 * 3540 * 3541 * @param {string|Component} child 3542 * The name or instance of a child to add. 3543 * 3544 * @param {Object} [options={}] 3545 * The key/value store of options that will get passed to children of 3546 * the child. 3547 * 3548 * @param {number} [index=this.children_.length] 3549 * The index to attempt to add a child into. 3550 * 3551 * @return {Component} 3552 * The `Component` that gets added as a child. When using a string the 3553 * `Component` will get created by this process. 3554 */ 3555 3556 3557 Component.prototype.addChild = function addChild(child) { 3558 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 3559 var index = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.children_.length; 3560 3561 var component = void 0; 3562 var componentName = void 0; 3563 3564 // If child is a string, create component with options 3565 if (typeof child === 'string') { 3566 componentName = toTitleCase(child); 3567 3568 var componentClassName = options.componentClass || componentName; 3569 3570 // Set name through options 3571 options.name = componentName; 3572 3573 // Create a new object & element for this controls set 3574 // If there's no .player_, this is a player 3575 var ComponentClass = Component.getComponent(componentClassName); 3576 3577 if (!ComponentClass) { 3578 throw new Error('Component ' + componentClassName + ' does not exist'); 3579 } 3580 3581 // data stored directly on the videojs object may be 3582 // misidentified as a component to retain 3583 // backwards-compatibility with 4.x. check to make sure the 3584 // component class can be instantiated. 3585 if (typeof ComponentClass !== 'function') { 3586 return null; 3587 } 3588 3589 component = new ComponentClass(this.player_ || this, options); 3590 3591 // child is a component instance 3592 } else { 3593 component = child; 3594 } 3595 3596 this.children_.splice(index, 0, component); 3597 3598 if (typeof component.id === 'function') { 3599 this.childIndex_[component.id()] = component; 3600 } 3601 3602 // If a name wasn't used to create the component, check if we can use the 3603 // name function of the component 3604 componentName = componentName || component.name && toTitleCase(component.name()); 3605 3606 if (componentName) { 3607 this.childNameIndex_[componentName] = component; 3608 } 3609 3610 // Add the UI object's element to the container div (box) 3611 // Having an element is not required 3612 if (typeof component.el === 'function' && component.el()) { 3613 var childNodes = this.contentEl().children; 3614 var refNode = childNodes[index] || null; 3615 3616 this.contentEl().insertBefore(component.el(), refNode); 3617 } 3618 3619 // Return so it can stored on parent object if desired. 3620 return component; 3621 }; 3622 3623 /** 3624 * Remove a child `Component` from this `Component`s list of children. Also removes 3625 * the child `Component`s element from this `Component`s element. 3626 * 3627 * @param {Component} component 3628 * The child `Component` to remove. 3629 */ 3630 3631 3632 Component.prototype.removeChild = function removeChild(component) { 3633 if (typeof component === 'string') { 3634 component = this.getChild(component); 3635 } 3636 3637 if (!component || !this.children_) { 3638 return; 3639 } 3640 3641 var childFound = false; 3642 3643 for (var i = this.children_.length - 1; i >= 0; i--) { 3644 if (this.children_[i] === component) { 3645 childFound = true; 3646 this.children_.splice(i, 1); 3647 break; 3648 } 3649 } 3650 3651 if (!childFound) { 3652 return; 3653 } 3654 3655 this.childIndex_[component.id()] = null; 3656 this.childNameIndex_[component.name()] = null; 3657 3658 var compEl = component.el(); 3659 3660 if (compEl && compEl.parentNode === this.contentEl()) { 3661 this.contentEl().removeChild(component.el()); 3662 } 3663 }; 3664 3665 /** 3666 * Add and initialize default child `Component`s based upon options. 3667 */ 3668 3669 3670 Component.prototype.initChildren = function initChildren() { 3671 var _this = this; 3672 3673 var children = this.options_.children; 3674 3675 if (children) { 3676 // `this` is `parent` 3677 var parentOptions = this.options_; 3678 3679 var handleAdd = function handleAdd(child) { 3680 var name = child.name; 3681 var opts = child.opts; 3682 3683 // Allow options for children to be set at the parent options 3684 // e.g. videojs(id, { controlBar: false }); 3685 // instead of videojs(id, { children: { controlBar: false }); 3686 if (parentOptions[name] !== undefined) { 3687 opts = parentOptions[name]; 3688 } 3689 3690 // Allow for disabling default components 3691 // e.g. options['children']['posterImage'] = false 3692 if (opts === false) { 3693 return; 3694 } 3695 3696 // Allow options to be passed as a simple boolean if no configuration 3697 // is necessary. 3698 if (opts === true) { 3699 opts = {}; 3700 } 3701 3702 // We also want to pass the original player options 3703 // to each component as well so they don't need to 3704 // reach back into the player for options later. 3705 opts.playerOptions = _this.options_.playerOptions; 3706 3707 // Create and add the child component. 3708 // Add a direct reference to the child by name on the parent instance. 3709 // If two of the same component are used, different names should be supplied 3710 // for each 3711 var newChild = _this.addChild(name, opts); 3712 3713 if (newChild) { 3714 _this[name] = newChild; 3715 } 3716 }; 3717 3718 // Allow for an array of children details to passed in the options 3719 var workingChildren = void 0; 3720 var Tech = Component.getComponent('Tech'); 3721 3722 if (Array.isArray(children)) { 3723 workingChildren = children; 3724 } else { 3725 workingChildren = Object.keys(children); 3726 } 3727 3728 workingChildren 3729 // children that are in this.options_ but also in workingChildren would 3730 // give us extra children we do not want. So, we want to filter them out. 3731 .concat(Object.keys(this.options_).filter(function (child) { 3732 return !workingChildren.some(function (wchild) { 3733 if (typeof wchild === 'string') { 3734 return child === wchild; 3735 } 3736 return child === wchild.name; 3737 }); 3738 })).map(function (child) { 3739 var name = void 0; 3740 var opts = void 0; 3741 3742 if (typeof child === 'string') { 3743 name = child; 3744 opts = children[name] || _this.options_[name] || {}; 3745 } else { 3746 name = child.name; 3747 opts = child; 3748 } 3749 3750 return { name: name, opts: opts }; 3751 }).filter(function (child) { 3752 // we have to make sure that child.name isn't in the techOrder since 3753 // techs are registerd as Components but can't aren't compatible 3754 // See https://github.com/videojs/video.js/issues/2772 3755 var c = Component.getComponent(child.opts.componentClass || toTitleCase(child.name)); 3756 3757 return c && !Tech.isTech(c); 3758 }).forEach(handleAdd); 3759 } 3760 }; 3761 3762 /** 3763 * Builds the default DOM class name. Should be overriden by sub-components. 3764 * 3765 * @return {string} 3766 * The DOM class name for this object. 3767 * 3768 * @abstract 3769 */ 3770 3771 3772 Component.prototype.buildCSSClass = function buildCSSClass() { 3773 // Child classes can include a function that does: 3774 // return 'CLASS NAME' + this._super(); 3775 return ''; 3776 }; 3777 3778 /** 3779 * Bind a listener to the component's ready state. 3780 * Different from event listeners in that if the ready event has already happened 3781 * it will trigger the function immediately. 3782 * 3783 * @return {Component} 3784 * Returns itself; method can be chained. 3785 */ 3786 3787 3788 Component.prototype.ready = function ready(fn) { 3789 var sync = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; 3790 3791 if (!fn) { 3792 return; 3793 } 3794 3795 if (!this.isReady_) { 3796 this.readyQueue_ = this.readyQueue_ || []; 3797 this.readyQueue_.push(fn); 3798 return; 3799 } 3800 3801 if (sync) { 3802 fn.call(this); 3803 } else { 3804 // Call the function asynchronously by default for consistency 3805 this.setTimeout(fn, 1); 3806 } 3807 }; 3808 3809 /** 3810 * Trigger all the ready listeners for this `Component`. 3811 * 3812 * @fires Component#ready 3813 */ 3814 3815 3816 Component.prototype.triggerReady = function triggerReady() { 3817 this.isReady_ = true; 3818 3819 // Ensure ready is triggerd asynchronously 3820 this.setTimeout(function () { 3821 var readyQueue = this.readyQueue_; 3822 3823 // Reset Ready Queue 3824 this.readyQueue_ = []; 3825 3826 if (readyQueue && readyQueue.length > 0) { 3827 readyQueue.forEach(function (fn) { 3828 fn.call(this); 3829 }, this); 3830 } 3831 3832 // Allow for using event listeners also 3833 /** 3834 * Triggered when a `Component` is ready. 3835 * 3836 * @event Component#ready 3837 * @type {EventTarget~Event} 3838 */ 3839 this.trigger('ready'); 3840 }, 1); 3841 }; 3842 3843 /** 3844 * Find a single DOM element matching a `selector`. This can be within the `Component`s 3845 * `contentEl()` or another custom context. 3846 * 3847 * @param {string} selector 3848 * A valid CSS selector, which will be passed to `querySelector`. 3849 * 3850 * @param {Element|string} [context=this.contentEl()] 3851 * A DOM element within which to query. Can also be a selector string in 3852 * which case the first matching element will get used as context. If 3853 * missing `this.contentEl()` gets used. If `this.contentEl()` returns 3854 * nothing it falls back to `document`. 3855 * 3856 * @return {Element|null} 3857 * the dom element that was found, or null 3858 * 3859 * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors) 3860 */ 3861 3862 3863 Component.prototype.$ = function $$$1(selector, context) { 3864 return $(selector, context || this.contentEl()); 3865 }; 3866 3867 /** 3868 * Finds all DOM element matching a `selector`. This can be within the `Component`s 3869 * `contentEl()` or another custom context. 3870 * 3871 * @param {string} selector 3872 * A valid CSS selector, which will be passed to `querySelectorAll`. 3873 * 3874 * @param {Element|string} [context=this.contentEl()] 3875 * A DOM element within which to query. Can also be a selector string in 3876 * which case the first matching element will get used as context. If 3877 * missing `this.contentEl()` gets used. If `this.contentEl()` returns 3878 * nothing it falls back to `document`. 3879 * 3880 * @return {NodeList} 3881 * a list of dom elements that were found 3882 * 3883 * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors) 3884 */ 3885 3886 3887 Component.prototype.$$ = function $$$$1(selector, context) { 3888 return $$(selector, context || this.contentEl()); 3889 }; 3890 3891 /** 3892 * Check if a component's element has a CSS class name. 3893 * 3894 * @param {string} classToCheck 3895 * CSS class name to check. 3896 * 3897 * @return {boolean} 3898 * - True if the `Component` has the class. 3899 * - False if the `Component` does not have the class` 3900 */ 3901 3902 3903 Component.prototype.hasClass = function hasClass$$1(classToCheck) { 3904 return hasClass(this.el_, classToCheck); 3905 }; 3906 3907 /** 3908 * Add a CSS class name to the `Component`s element. 3909 * 3910 * @param {string} classToAdd 3911 * CSS class name to add 3912 */ 3913 3914 3915 Component.prototype.addClass = function addClass$$1(classToAdd) { 3916 addClass(this.el_, classToAdd); 3917 }; 3918 3919 /** 3920 * Remove a CSS class name from the `Component`s element. 3921 * 3922 * @param {string} classToRemove 3923 * CSS class name to remove 3924 */ 3925 3926 3927 Component.prototype.removeClass = function removeClass$$1(classToRemove) { 3928 removeClass(this.el_, classToRemove); 3929 }; 3930 3931 /** 3932 * Add or remove a CSS class name from the component's element. 3933 * - `classToToggle` gets added when {@link Component#hasClass} would return false. 3934 * - `classToToggle` gets removed when {@link Component#hasClass} would return true. 3935 * 3936 * @param {string} classToToggle 3937 * The class to add or remove based on (@link Component#hasClass} 3938 * 3939 * @param {boolean|Dom~predicate} [predicate] 3940 * An {@link Dom~predicate} function or a boolean 3941 */ 3942 3943 3944 Component.prototype.toggleClass = function toggleClass$$1(classToToggle, predicate) { 3945 toggleClass(this.el_, classToToggle, predicate); 3946 }; 3947 3948 /** 3949 * Show the `Component`s element if it is hidden by removing the 3950 * 'vjs-hidden' class name from it. 3951 */ 3952 3953 3954 Component.prototype.show = function show() { 3955 this.removeClass('vjs-hidden'); 3956 }; 3957 3958 /** 3959 * Hide the `Component`s element if it is currently showing by adding the 3960 * 'vjs-hidden` class name to it. 3961 */ 3962 3963 3964 Component.prototype.hide = function hide() { 3965 this.addClass('vjs-hidden'); 3966 }; 3967 3968 /** 3969 * Lock a `Component`s element in its visible state by adding the 'vjs-lock-showing' 3970 * class name to it. Used during fadeIn/fadeOut. 3971 * 3972 * @private 3973 */ 3974 3975 3976 Component.prototype.lockShowing = function lockShowing() { 3977 this.addClass('vjs-lock-showing'); 3978 }; 3979 3980 /** 3981 * Unlock a `Component`s element from its visible state by removing the 'vjs-lock-showing' 3982 * class name from it. Used during fadeIn/fadeOut. 3983 * 3984 * @private 3985 */ 3986 3987 3988 Component.prototype.unlockShowing = function unlockShowing() { 3989 this.removeClass('vjs-lock-showing'); 3990 }; 3991 3992 /** 3993 * Get the value of an attribute on the `Component`s element. 3994 * 3995 * @param {string} attribute 3996 * Name of the attribute to get the value from. 3997 * 3998 * @return {string|null} 3999 * - The value of the attribute that was asked for. 4000 * - Can be an empty string on some browsers if the attribute does not exist 4001 * or has no value 4002 * - Most browsers will return null if the attibute does not exist or has 4003 * no value. 4004 * 4005 * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute} 4006 */ 4007 4008 4009 Component.prototype.getAttribute = function getAttribute$$1(attribute) { 4010 return getAttribute(this.el_, attribute); 4011 }; 4012 4013 /** 4014 * Set the value of an attribute on the `Component`'s element 4015 * 4016 * @param {string} attribute 4017 * Name of the attribute to set. 4018 * 4019 * @param {string} value 4020 * Value to set the attribute to. 4021 * 4022 * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute} 4023 */ 4024 4025 4026 Component.prototype.setAttribute = function setAttribute$$1(attribute, value) { 4027 setAttribute(this.el_, attribute, value); 4028 }; 4029 4030 /** 4031 * Remove an attribute from the `Component`s element. 4032 * 4033 * @param {string} attribute 4034 * Name of the attribute to remove. 4035 * 4036 * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttribute} 4037 */ 4038 4039 4040 Component.prototype.removeAttribute = function removeAttribute$$1(attribute) { 4041 removeAttribute(this.el_, attribute); 4042 }; 4043 4044 /** 4045 * Get or set the width of the component based upon the CSS styles. 4046 * See {@link Component#dimension} for more detailed information. 4047 * 4048 * @param {number|string} [num] 4049 * The width that you want to set postfixed with '%', 'px' or nothing. 4050 * 4051 * @param {boolean} [skipListeners] 4052 * Skip the componentresize event trigger 4053 * 4054 * @return {number|string} 4055 * The width when getting, zero if there is no width. Can be a string 4056 * postpixed with '%' or 'px'. 4057 */ 4058 4059 4060 Component.prototype.width = function width(num, skipListeners) { 4061 return this.dimension('width', num, skipListeners); 4062 }; 4063 4064 /** 4065 * Get or set the height of the component based upon the CSS styles. 4066 * See {@link Component#dimension} for more detailed information. 4067 * 4068 * @param {number|string} [num] 4069 * The height that you want to set postfixed with '%', 'px' or nothing. 4070 * 4071 * @param {boolean} [skipListeners] 4072 * Skip the componentresize event trigger 4073 * 4074 * @return {number|string} 4075 * The width when getting, zero if there is no width. Can be a string 4076 * postpixed with '%' or 'px'. 4077 */ 4078 4079 4080 Component.prototype.height = function height(num, skipListeners) { 4081 return this.dimension('height', num, skipListeners); 4082 }; 4083 4084 /** 4085 * Set both the width and height of the `Component` element at the same time. 4086 * 4087 * @param {number|string} width 4088 * Width to set the `Component`s element to. 4089 * 4090 * @param {number|string} height 4091 * Height to set the `Component`s element to. 4092 */ 4093 4094 4095 Component.prototype.dimensions = function dimensions(width, height) { 4096 // Skip componentresize listeners on width for optimization 4097 this.width(width, true); 4098 this.height(height); 4099 }; 4100 4101 /** 4102 * Get or set width or height of the `Component` element. This is the shared code 4103 * for the {@link Component#width} and {@link Component#height}. 4104 * 4105 * Things to know: 4106 * - If the width or height in an number this will return the number postfixed with 'px'. 4107 * - If the width/height is a percent this will return the percent postfixed with '%' 4108 * - Hidden elements have a width of 0 with `window.getComputedStyle`. This function 4109 * defaults to the `Component`s `style.width` and falls back to `window.getComputedStyle`. 4110 * See [this]{@link http://www.foliotek.com/devblog/getting-the-width-of-a-hidden-element-with-jquery-using-width/} 4111 * for more information 4112 * - If you want the computed style of the component, use {@link Component#currentWidth} 4113 * and {@link {Component#currentHeight} 4114 * 4115 * @fires Component#componentresize 4116 * 4117 * @param {string} widthOrHeight 4118 8 'width' or 'height' 4119 * 4120 * @param {number|string} [num] 4121 8 New dimension 4122 * 4123 * @param {boolean} [skipListeners] 4124 * Skip componentresize event trigger 4125 * 4126 * @return {number} 4127 * The dimension when getting or 0 if unset 4128 */ 4129 4130 4131 Component.prototype.dimension = function dimension(widthOrHeight, num, skipListeners) { 4132 if (num !== undefined) { 4133 // Set to zero if null or literally NaN (NaN !== NaN) 4134 if (num === null || num !== num) { 4135 num = 0; 4136 } 4137 4138 // Check if using css width/height (% or px) and adjust 4139 if (('' + num).indexOf('%') !== -1 || ('' + num).indexOf('px') !== -1) { 4140 this.el_.style[widthOrHeight] = num; 4141 } else if (num === 'auto') { 4142 this.el_.style[widthOrHeight] = ''; 4143 } else { 4144 this.el_.style[widthOrHeight] = num + 'px'; 4145 } 4146 4147 // skipListeners allows us to avoid triggering the resize event when setting both width and height 4148 if (!skipListeners) { 4149 /** 4150 * Triggered when a component is resized. 4151 * 4152 * @event Component#componentresize 4153 * @type {EventTarget~Event} 4154 */ 4155 this.trigger('componentresize'); 4156 } 4157 4158 return; 4159 } 4160 4161 // Not setting a value, so getting it 4162 // Make sure element exists 4163 if (!this.el_) { 4164 return 0; 4165 } 4166 4167 // Get dimension value from style 4168 var val = this.el_.style[widthOrHeight]; 4169 var pxIndex = val.indexOf('px'); 4170 4171 if (pxIndex !== -1) { 4172 // Return the pixel value with no 'px' 4173 return parseInt(val.slice(0, pxIndex), 10); 4174 } 4175 4176 // No px so using % or no style was set, so falling back to offsetWidth/height 4177 // If component has display:none, offset will return 0 4178 // TODO: handle display:none and no dimension style using px 4179 return parseInt(this.el_['offset' + toTitleCase(widthOrHeight)], 10); 4180 }; 4181 4182 /** 4183 * Get the width or the height of the `Component` elements computed style. Uses 4184 * `window.getComputedStyle`. 4185 * 4186 * @param {string} widthOrHeight 4187 * A string containing 'width' or 'height'. Whichever one you want to get. 4188 * 4189 * @return {number} 4190 * The dimension that gets asked for or 0 if nothing was set 4191 * for that dimension. 4192 */ 4193 4194 4195 Component.prototype.currentDimension = function currentDimension(widthOrHeight) { 4196 var computedWidthOrHeight = 0; 4197 4198 if (widthOrHeight !== 'width' && widthOrHeight !== 'height') { 4199 throw new Error('currentDimension only accepts width or height value'); 4200 } 4201 4202 if (typeof
vendor: 11,597 bytes, lines 4202-4543
4202window_1.getComputedStyle === 'function') { 4203 var computedStyle = window_1.getComputedStyle(this.el_); 4204 4205 computedWidthOrHeight = computedStyle.getPropertyValue(widthOrHeight) || computedStyle[widthOrHeight]; 4206 } 4207 4208 // remove 'px' from variable and parse as integer 4209 computedWidthOrHeight = parseFloat(computedWidthOrHeight); 4210 4211 // if the computed value is still 0, it's possible that the browser is lying 4212 // and we want to check the offset values. 4213 // This code also runs on IE8 and wherever getComputedStyle doesn't exist. 4214 if (computedWidthOrHeight === 0) { 4215 var rule = 'offset' + toTitleCase(widthOrHeight); 4216 4217 computedWidthOrHeight = this.el_[rule]; 4218 } 4219 4220 return computedWidthOrHeight; 4221 }; 4222 4223 /** 4224 * An object that contains width and height values of the `Component`s 4225 * computed style. Uses `window.getComputedStyle`. 4226 * 4227 * @typedef {Object} Component~DimensionObject 4228 * 4229 * @property {number} width 4230 * The width of the `Component`s computed style. 4231 * 4232 * @property {number} height 4233 * The height of the `Component`s computed style. 4234 */ 4235 4236 /** 4237 * Get an object that contains width and height values of the `Component`s 4238 * computed style. 4239 * 4240 * @return {Component~DimensionObject} 4241 * The dimensions of the components element 4242 */ 4243 4244 4245 Component.prototype.currentDimensions = function currentDimensions() { 4246 return { 4247 width: this.currentDimension('width'), 4248 height: this.currentDimension('height') 4249 }; 4250 }; 4251 4252 /** 4253 * Get the width of the `Component`s computed style. Uses `window.getComputedStyle`. 4254 * 4255 * @return {number} width 4256 * The width of the `Component`s computed style. 4257 */ 4258 4259 4260 Component.prototype.currentWidth = function currentWidth() { 4261 return this.currentDimension('width'); 4262 }; 4263 4264 /** 4265 * Get the height of the `Component`s computed style. Uses `window.getComputedStyle`. 4266 * 4267 * @return {number} height 4268 * The height of the `Component`s computed style. 4269 */ 4270 4271 4272 Component.prototype.currentHeight = function currentHeight() { 4273 return this.currentDimension('height'); 4274 }; 4275 4276 /** 4277 * Set the focus to this component 4278 */ 4279 4280 4281 Component.prototype.focus = function focus() { 4282 this.el_.focus(); 4283 }; 4284 4285 /** 4286 * Remove the focus from this component 4287 */ 4288 4289 4290 Component.prototype.blur = function blur() { 4291 this.el_.blur(); 4292 }; 4293 4294 /** 4295 * Emit a 'tap' events when touch event support gets detected. This gets used to 4296 * support toggling the controls through a tap on the video. They get enabled 4297 * because every sub-component would have extra overhead otherwise. 4298 * 4299 * @private 4300 * @fires Component#tap 4301 * @listens Component#touchstart 4302 * @listens Component#touchmove 4303 * @listens Component#touchleave 4304 * @listens Component#touchcancel 4305 * @listens Component#touchend 4306 */ 4307 4308 4309 Component.prototype.emitTapEvents = function emitTapEvents() { 4310 // Track the start time so we can determine how long the touch lasted 4311 var touchStart = 0; 4312 var firstTouch = null; 4313 4314 // Maximum movement allowed during a touch event to still be considered a tap 4315 // Other popular libs use anywhere from 2 (hammer.js) to 15, 4316 // so 10 seems like a nice, round number. 4317 var tapMovementThreshold = 10; 4318 4319 // The maximum length a touch can be while still being considered a tap 4320 var touchTimeThreshold = 200; 4321 4322 var couldBeTap = void 0; 4323 4324 this.on('touchstart', function (event) { 4325 // If more than one finger, don't consider treating this as a click 4326 if (event.touches.length === 1) { 4327 // Copy pageX/pageY from the object 4328 firstTouch = { 4329 pageX: event.touches[0].pageX, 4330 pageY: event.touches[0].pageY 4331 }; 4332 // Record start time so we can detect a tap vs. "touch and hold" 4333 touchStart = new Date().getTime(); 4334 // Reset couldBeTap tracking 4335 couldBeTap = true; 4336 } 4337 }); 4338 4339 this.on('touchmove', function (event) { 4340 // If more than one finger, don't consider treating this as a click 4341 if (event.touches.length > 1) { 4342 couldBeTap = false; 4343 } else if (firstTouch) { 4344 // Some devices will throw touchmoves for all but the slightest of taps. 4345 // So, if we moved only a small distance, this could still be a tap 4346 var xdiff = event.touches[0].pageX - firstTouch.pageX; 4347 var ydiff = event.touches[0].pageY - firstTouch.pageY; 4348 var touchDistance = Math.sqrt(xdiff * xdiff + ydiff * ydiff); 4349 4350 if (touchDistance > tapMovementThreshold) { 4351 couldBeTap = false; 4352 } 4353 } 4354 }); 4355 4356 var noTap = function noTap() { 4357 couldBeTap = false; 4358 }; 4359 4360 // TODO: Listen to the original target. http://youtu.be/DujfpXOKUp8?t=13m8s 4361 this.on('touchleave', noTap); 4362 this.on('touchcancel', noTap); 4363 4364 // When the touch ends, measure how long it took and trigger the appropriate 4365 // event 4366 this.on('touchend', function (event) { 4367 firstTouch = null; 4368 // Proceed only if the touchmove/leave/cancel event didn't happen 4369 if (couldBeTap === true) { 4370 // Measure how long the touch lasted 4371 var touchTime = new Date().getTime() - touchStart; 4372 4373 // Make sure the touch was less than the threshold to be considered a tap 4374 if (touchTime < touchTimeThreshold) { 4375 // Don't let browser turn this into a click 4376 event.preventDefault(); 4377 /** 4378 * Triggered when a `Component` is tapped. 4379 * 4380 * @event Component#tap 4381 * @type {EventTarget~Event} 4382 */ 4383 this.trigger('tap'); 4384 // It may be good to copy the touchend event object and change the 4385 // type to tap, if the other event properties aren't exact after 4386 // Events.fixEvent runs (e.g. event.target) 4387 } 4388 } 4389 }); 4390 }; 4391 4392 /** 4393 * This function reports user activity whenever touch events happen. This can get 4394 * turned off by any sub-components that wants touch events to act another way. 4395 * 4396 * Report user touch activity when touch events occur. User activity gets used to 4397 * determine when controls should show/hide. It is simple when it comes to mouse 4398 * events, because any mouse event should show the controls. So we capture mouse 4399 * events that bubble up to the player and report activity when that happens. 4400 * With touch events it isn't as easy as `touchstart` and `touchend` toggle player 4401 * controls. So touch events can't help us at the player level either. 4402 * 4403 * User activity gets checked asynchronously. So what could happen is a tap event 4404 * on the video turns the controls off. Then the `touchend` event bubbles up to 4405 * the player. Which, if it reported user activity, would turn the controls right 4406 * back on. We also don't want to completely block touch events from bubbling up. 4407 * Furthermore a `touchmove` event and anything other than a tap, should not turn 4408 * controls back on. 4409 * 4410 * @listens Component#touchstart 4411 * @listens Component#touchmove 4412 * @listens Component#touchend 4413 * @listens Component#touchcancel 4414 */ 4415 4416 4417 Component.prototype.enableTouchActivity = function enableTouchActivity() { 4418 // Don't continue if the root player doesn't support reporting user activity 4419 if (!this.player() || !this.player().reportUserActivity) { 4420 return; 4421 } 4422 4423 // listener for reporting that the user is active 4424 var report = bind(this.player(), this.player().reportUserActivity); 4425 4426 var touchHolding = void 0; 4427 4428 this.on('touchstart', function () { 4429 report(); 4430 // For as long as the they are touching the device or have their mouse down, 4431 // we consider them active even if they're not moving their finger or mouse. 4432 // So we want to continue to update that they are active 4433 this.clearInterval(touchHolding); 4434 // report at the same interval as activityCheck 4435 touchHolding = this.setInterval(report, 250); 4436 }); 4437 4438 var touchEnd = function touchEnd(event) { 4439 report(); 4440 // stop the interval that maintains activity if the touch is holding 4441 this.clearInterval(touchHolding); 4442 }; 4443 4444 this.on('touchmove', report); 4445 this.on('touchend', touchEnd); 4446 this.on('touchcancel', touchEnd); 4447 }; 4448 4449 /** 4450 * A callback that has no parameters and is bound into `Component`s context. 4451 * 4452 * @callback Component~GenericCallback 4453 * @this Component 4454 */ 4455 4456 /** 4457 * Creates a function that runs after an `x` millisecond timeout. This function is a 4458 * wrapper around `window.setTimeout`. There are a few reasons to use this one 4459 * instead though: 4460 * 1. It gets cleared via {@link Component#clearTimeout} when 4461 * {@link Component#dispose} gets called. 4462 * 2. The function callback will gets turned into a {@link Component~GenericCallback} 4463 * 4464 * > Note: You can use `window.clearTimeout` on the id returned by this function. This 4465 * will cause its dispose listener not to get cleaned up! Please use 4466 * {@link Component#clearTimeout} or {@link Component#dispose}. 4467 * 4468 * @param {Component~GenericCallback} fn 4469 * The function that will be run after `timeout`. 4470 * 4471 * @param {number} timeout 4472 * Timeout in milliseconds to delay before executing the specified function. 4473 * 4474 * @return {number} 4475 * Returns a timeout ID that gets used to identify the timeout. It can also 4476 * get used in {@link Component#clearTimeout} to clear the timeout that 4477 * was set. 4478 * 4479 * @listens Component#dispose 4480 * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout} 4481 */ 4482 4483 4484 Component.prototype.setTimeout = function setTimeout(fn, timeout) { 4485 var _this2 = this; 4486 4487 fn = bind(this, fn); 4488 4489 var timeoutId = window_1.setTimeout(fn, timeout); 4490 var disposeFn = function disposeFn() { 4491 return _this2.clearTimeout(timeoutId); 4492 }; 4493 4494 disposeFn.guid = 'vjs-timeout-' + timeoutId; 4495 4496 this.on('dispose', disposeFn); 4497 4498 return timeoutId; 4499 }; 4500 4501 /** 4502 * Clears a timeout that gets created via `window.setTimeout` or 4503 * {@link Component#setTimeout}. If you set a timeout via {@link Component#setTimeout} 4504 * use this function instead of `window.clearTimout`. If you don't your dispose 4505 * listener will not get cleaned up until {@link Component#dispose}! 4506 * 4507 * @param {number} timeoutId 4508 * The id of the timeout to clear. The return value of 4509 * {@link Component#setTimeout} or `window.setTimeout`. 4510 * 4511 * @return {number} 4512 * Returns the timeout id that was cleared. 4513 * 4514 * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearTimeout} 4515 */ 4516 4517 4518 Component.prototype.clearTimeout = function clearTimeout(timeoutId) { 4519 window_1.clearTimeout(timeoutId); 4520 4521 var disposeFn = function disposeFn() {}; 4522 4523 disposeFn.guid = 'vjs-timeout-' + timeoutId; 4524 4525 this.off('dispose', disposeFn); 4526 4527 return timeoutId; 4528 }; 4529 4530 /** 4531 * Creates a function that gets run every `x` milliseconds. This function is a wrapper 4532 * around `window.setInterval`. There are a few reasons to use this one instead though. 4533 * 1. It gets cleared via {@link Component#clearInterval} when 4534 * {@link Component#dispose} gets called. 4535 * 2. The function callback will be a {@link Component~GenericCallback} 4536 * 4537 * @param {Component~GenericCallback} fn 4538 * The function to run every `x` seconds. 4539 * 4540 * @param {number} interval 4541 * Execute the specified function every `x` milliseconds. 4542 * 4543 * @return {number}
4544 * Returns an id that can be used to identify the interval. It can also be be used in 4545 * {@link Component#clearInterval} to clear the interval. 4546 * 4547 * @listens Component#dispose 4548 * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval} 4549 */ 4550 4551 4552 Component.prototype.setInterval = function setInterval(fn, interval) { 4553 var _this3 = this; 4554 4555 fn = bind(this, fn); 4556 4557 var intervalId = window_1.setInterval(fn, interval); 4558 4559 var disposeFn = function disposeFn() { 4560 return _this3.clearInterval(intervalId); 4561 }; 4562 4563 disposeFn.guid = 'vjs-interval-' + intervalId; 4564 4565 this.on('dispose', disposeFn); 4566 4567 return intervalId; 4568 }; 4569 4570 /** 4571 * Clears an interval that gets created via `window.setInterval` or 4572 * {@link Component#setInterval}. If you set an inteval via {@link Component#setInterval} 4573 * use this function instead of `window.clearInterval`. If you don't your dispose 4574 * listener will not get cleaned up until {@link Component#dispose}! 4575 * 4576 * @param {number} intervalId 4577 * The id of the interval to clear. The return value of 4578 * {@link Component#setInterval} or `window.setInterval`. 4579 * 4580 * @return {number} 4581 * Returns the interval id that was cleared. 4582 * 4583 * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearInterval} 4584 */ 4585 4586 4587 Component.prototype.clearInterval = function clearInterval(intervalId) { 4588 window_1.clearInterval(intervalId); 4589 4590 var disposeFn = function disposeFn() {}; 4591 4592 disposeFn.guid = 'vjs-interval-' + intervalId; 4593 4594 this.off('dispose', disposeFn); 4595 4596 return intervalId; 4597 }; 4598 4599 /** 4600 * Queues up a callback to be passed to requestAnimationFrame (rAF), but 4601 * with a few extra bonuses: 4602 * 4603 * - Supports browsers that do not support rAF by falling back to 4604 * {@link Component#setTimeout}. 4605 * 4606 * - The callback is turned into a {@link Component~GenericCallback} (i.e. 4607 * bound to the component). 4608 * 4609 * - Automatic cancellation of the rAF callback is handled if the component 4610 * is disposed before it is called. 4611 * 4612 * @param {Component~GenericCallback} fn 4613 * A function that will be bound to this component and executed just 4614 * before the browser's next repaint. 4615 * 4616 * @return {number} 4617 * Returns an rAF ID that gets used to identify the timeout. It can 4618 * also be used in {@link Component#cancelAnimationFrame} to cancel 4619 * the animation frame callback. 4620 * 4621 * @listens Component#dispose 4622 * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame} 4623 */ 4624 4625 4626 Component.prototype.requestAnimationFrame = function requestAnimationFrame(fn) { 4627 var _this4 = this; 4628 4629 if (this.supportsRaf_) { 4630 fn = bind(this, fn); 4631 4632 var id = window_1.requestAnimationFrame(fn); 4633 var disposeFn = function disposeFn() { 4634 return _this4.cancelAnimationFrame(id); 4635 }; 4636 4637 disposeFn.guid = 'vjs-raf-' + id; 4638 this.on('dispose', disposeFn); 4639 4640 return id; 4641 } 4642 4643 // Fall back to using a timer. 4644 return this.setTimeout(fn, 1000 / 60); 4645 }; 4646 4647 /** 4648 * Cancels a queued callback passed to {@link Component#requestAnimationFrame} 4649 * (rAF). 4650 * 4651 * If you queue an rAF callback via {@link Component#requestAnimationFrame}, 4652 * use this function instead of `window.cancelAnimationFrame`. If you don't, 4653 * your dispose listener will not get cleaned up until {@link Component#dispose}! 4654 * 4655 * @param {number} id 4656 * The rAF ID to clear. The return value of {@link Component#requestAnimationFrame}. 4657 * 4658 * @return {number} 4659 * Returns the rAF ID that was cleared. 4660 * 4661 * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/window/cancelAnimationFrame} 4662 */ 4663 4664 4665 Component.prototype.cancelAnimationFrame = function cancelAnimationFrame(id) { 4666 if (this.supportsRaf_) { 4667 window_1.cancelAnimationFrame(id); 4668 4669 var disposeFn = function disposeFn() {}; 4670 4671 disposeFn.guid = 'vjs-raf-' + id; 4672 4673 this.off('dispose', disposeFn); 4674 4675 return id; 4676 } 4677 4678 // Fall back to using a timer. 4679 return this.clearTimeout(id); 4680 }; 4681 4682 /** 4683 * Register a `Component` with `videojs` given the name and the component. 4684 * 4685 * > NOTE: {@link Tech}s should not be registered as a `Component`. {@link Tech}s 4686 * should be registered using {@link Tech.registerTech} or
4687 * {@link videojs:videojs.registerTech}. 4688 * 4689 * > NOTE: This function can also be seen on videojs as 4690 * {@link videojs:videojs.registerComponent}. 4691 * 4692 * @param {string} name 4693 * The name of the `Component` to register. 4694 * 4695 * @param {Component} ComponentToRegister 4696 * The `Component` class to register. 4697 * 4698 * @return {Component} 4699 * The `Component` that was registered. 4700 */ 4701 4702 4703 Component.registerComponent = function registerComponent(name, ComponentToRegister) { 4704 if (typeof name !== 'string' || !name) { 4705 throw new Error('Illegal component name, "' + name + '"; must be a non-empty string.'); 4706 } 4707 4708 var Tech = Component.getComponent('Tech'); 4709 4710 // We need to make sure this check is only done if Tech has been registered. 4711 var isTech = Tech && Tech.isTech(ComponentToRegister); 4712 var isComp = Component === ComponentToRegister || Component.prototype.isPrototypeOf(ComponentToRegister.prototype); 4713 4714 if (isTech || !isComp) { 4715 var reason = void 0; 4716 4717 if (isTech) { 4718 reason = 'techs must be registered using Tech.registerTech()'; 4719 } else { 4720 reason = 'must be a Component subclass'; 4721 } 4722 4723 throw new Error('Illegal component, "' + name + '"; ' + reason + '.'); 4724 } 4725 4726 name = toTitleCase(name); 4727 4728 if (!Component.components_) { 4729 Component.components_ = {}; 4730 } 4731 4732 var Player = Component.getComponent('Player'); 4733 4734 if (name === 'Player' && Player && Player.players) { 4735 var players = Player.players; 4736 var playerNames = Object.keys(players); 4737 4738 // If we have players that were disposed, then their name will still be 4739 // in Players.players. So, we must loop through and verify that the value 4740 // for each item is not null. This allows registration of the Player component 4741 // after all players have been disposed or before any were created. 4742 if (players && playerNames.length > 0 && playerNames.map(function (pname) { 4743 return players[pname]; 4744 }).every(Boolean)) { 4745 throw new Error('Can not register Player component after player has been created.'); 4746 } 4747 } 4748 4749 Component.components_[name] = ComponentToRegister; 4750 4751 return ComponentToRegister; 4752 }; 4753 4754 /** 4755 * Get a `Component` based on the name it was registered with. 4756 * 4757 * @param {string} name 4758 * The Name of the component to get. 4759 * 4760 * @return {Component} 4761 * The `Component` that got registered under the given name. 4762 * 4763 * @deprecated In `videojs` 6 this will not return `Component`s that were not 4764 * registered using {@link Component.registerComponent}. Currently we 4765 * check the global `videojs` object for a `Component` name and 4766 * return that if it exists. 4767 */ 4768 4769 4770 Component.getComponent = function getComponent(name) { 4771 if (!name) { 4772 return; 4773 } 4774 4775 name = toTitleCase(name); 4776 4777 if (Component.components_ && Component.components_[name]) { 4778 return Component.components_[name]; 4779 } 4780 }; 4781 4782 return Component; 4783}(); 4784 4785/** 4786 * Whether or not this component supports `requestAnimationFrame`. 4787 * 4788 * This is exposed primarily for testing purposes. 4789 * 4790 * @private 4791 * @type {Boolean} 4792 */ 4793 4794 4795Component.prototype.supportsRaf_ = typeof window_1.requestAnimationFrame === 'function' && typeof window_1.cancelAnimationFrame === 'function'; 4796 4797Component.registerComponent('Component', Component); 4798 4799/** 4800 * @file time-ranges.js 4801 * @module time-ranges 4802 */ 4803 4804/** 4805 * Returns the time for the specified index at the start or end 4806 * of a TimeRange object. 4807 * 4808 * @function time-ranges:indexFunction 4809 * 4810 * @param {number} [index=0] 4811 * The range number to return the time for. 4812 * 4813 * @return {number} 4814 * The time that offset at the specified index. 4815 * 4816 * @depricated index must be set to a value, in the future this will throw an error. 4817 */ 4818 4819/** 4820 * An object that contains ranges of time for various reasons. 4821 * 4822 * @typedef {Object} TimeRange 4823 * 4824 * @property {number} length 4825 * The number of time ranges represented by this Object 4826 * 4827 * @property {time-ranges:indexFunction} start 4828 * Returns the time offset at which a specified time range begins. 4829 * 4830 * @property {time-ranges:indexFunction} end 4831 * Returns the time offset at which a specified time range begins. 4832 * 4833 * @see https://developer.mozilla.org/en-US/docs/Web/API/TimeRanges 4834 */ 4835
vendor: 6,212 bytes, lines 4836-5046
4836/** 4837 * Check if any of the time ranges are over the maximum index. 4838 * 4839 * @param {string} fnName 4840 * The function name to use for logging 4841 * 4842 * @param {number} index 4843 * The index to check 4844 * 4845 * @param {number} maxIndex 4846 * The maximum possible index 4847 * 4848 * @throws {Error} if the timeRanges provided are over the maxIndex 4849 */ 4850function rangeCheck(fnName, index, maxIndex) { 4851 if (typeof index !== 'number' || index < 0 || index > maxIndex) { 4852 throw new Error('Failed to execute \'' + fnName + '\' on \'TimeRanges\': The index provided (' + index + ') is non-numeric or out of bounds (0-' + maxIndex + ').'); 4853 } 4854} 4855 4856/** 4857 * Check if any of the time ranges are over the maximum index. 4858 * 4859 * @param {string} fnName 4860 * The function name to use for logging 4861 * 4862 * @param {string} valueIndex 4863 * The proprety that should be used to get the time. should be 'start' or 'end' 4864 * 4865 * @param {Array} ranges 4866 * An array of time ranges 4867 * 4868 * @param {Array} [rangeIndex=0] 4869 * The index to start the search at 4870 * 4871 * @return {number} 4872 * The time that offset at the specified index. 4873 * 4874 * 4875 * @depricated rangeIndex must be set to a value, in the future this will throw an error. 4876 * @throws {Error} if rangeIndex is more than the length of ranges 4877 */ 4878function getRange(fnName, valueIndex, ranges, rangeIndex) { 4879 rangeCheck(fnName, rangeIndex, ranges.length - 1); 4880 return ranges[rangeIndex][valueIndex]; 4881} 4882 4883/** 4884 * Create a time range object givent ranges of time. 4885 * 4886 * @param {Array} [ranges] 4887 * An array of time ranges. 4888 */ 4889function createTimeRangesObj(ranges) { 4890 if (ranges === undefined || ranges.length === 0) { 4891 return { 4892 length: 0, 4893 start: function start() { 4894 throw new Error('This TimeRanges object is empty'); 4895 }, 4896 end: function end() { 4897 throw new Error('This TimeRanges object is empty'); 4898 } 4899 }; 4900 } 4901 return { 4902 length: ranges.length, 4903 start: getRange.bind(null, 'start', 0, ranges), 4904 end: getRange.bind(null, 'end', 1, ranges) 4905 }; 4906} 4907 4908/** 4909 * Should create a fake `TimeRange` object which mimics an HTML5 time range instance. 4910 * 4911 * @param {number|Array} start 4912 * The start of a single range or an array of ranges 4913 * 4914 * @param {number} end 4915 * The end of a single range. 4916 * 4917 * @private 4918 */ 4919function createTimeRanges(start, end) { 4920 if (Array.isArray(start)) { 4921 return createTimeRangesObj(start); 4922 } else if (start === undefined || end === undefined) { 4923 return createTimeRangesObj(); 4924 } 4925 return createTimeRangesObj([[start, end]]); 4926} 4927 4928/** 4929 * @file buffer.js 4930 * @module buffer 4931 */ 4932/** 4933 * Compute the percentage of the media that has been buffered. 4934 * 4935 * @param {TimeRange} buffered 4936 * The current `TimeRange` object representing buffered time ranges 4937 * 4938 * @param {number} duration 4939 * Total duration of the media 4940 * 4941 * @return {number} 4942 * Percent buffered of the total duration in decimal form. 4943 */ 4944function bufferedPercent(buffered, duration) { 4945 var bufferedDuration = 0; 4946 var start = void 0; 4947 var end = void 0; 4948 4949 if (!duration) { 4950 return 0; 4951 } 4952 4953 if (!buffered || !buffered.length) { 4954 buffered = createTimeRanges(0, 0); 4955 } 4956 4957 for (var i = 0; i < buffered.length; i++) { 4958 start = buffered.start(i); 4959 end = buffered.end(i); 4960 4961 // buffered end can be bigger than duration by a very small fraction 4962 if (end > duration) { 4963 end = duration; 4964 } 4965 4966 bufferedDuration += end - start; 4967 } 4968 4969 return bufferedDuration / duration; 4970} 4971 4972/** 4973 * @file fullscreen-api.js 4974 * @module fullscreen-api 4975 * @private 4976 */ 4977/** 4978 * Store the browser-specific methods for the fullscreen API. 4979 * 4980 * @type {Object} 4981 * @see [Specification]{@link https://fullscreen.spec.whatwg.org} 4982 * @see [Map Approach From Screenfull.js]{@link https://github.com/sindresorhus/screenfull.js} 4983 */ 4984var FullscreenApi = {}; 4985 4986// browser API methods 4987var apiMap = [['requestFullscreen', 'exitFullscreen', 'fullscreenElement', 'fullscreenEnabled', 'fullscreenchange', 'fullscreenerror'], 4988// WebKit 4989['webkitRequestFullscreen', 'webkitExitFullscreen', 'webkitFullscreenElement', 'webkitFullscreenEnabled', 'webkitfullscreenchange', 'webkitfullscreenerror'], 4990// Old WebKit (Safari 5.1) 4991['webkitRequestFullScreen', 'webkitCancelFullScreen', 'webkitCurrentFullScreenElement', 'webkitCancelFullScreen', 'webkitfullscreenchange', 'webkitfullscreenerror'], 4992// Mozilla 4993['mozRequestFullScreen', 'mozCancelFullScreen', 'mozFullScreenElement', 'mozFullScreenEnabled', 'mozfullscreenchange', 'mozfullscreenerror'], 4994// Microsoft 4995['msRequestFullscreen', 'msExitFullscreen', 'msFullscreenElement', 'msFullscreenEnabled', 'MSFullscreenChange', 'MSFullscreenError']]; 4996 4997var specApi = apiMap[0]; 4998var browserApi = void 0; 4999 5000// determine the supported set of functions 5001for (var i = 0; i < apiMap.length; i++) { 5002 // check for exitFullscreen function 5003 if (apiMap[i][1] in document_1) { 5004 browserApi = apiMap[i]; 5005 break; 5006 } 5007} 5008 5009// map the browser API names to the spec API names 5010if (browserApi) { 5011 for (var _i = 0; _i < browserApi.length; _i++) { 5012 FullscreenApi[specApi[_i]] = browserApi[_i]; 5013 } 5014} 5015 5016/** 5017 * @file media-error.js 5018 */ 5019/** 5020 * A Custom `MediaError` class which mimics the standard HTML5 `MediaError` class. 5021 * 5022 * @param {number|string|Object|MediaError} value 5023 * This can be of multiple types: 5024 * - number: should be a standard error code 5025 * - string: an error message (the code will be 0) 5026 * - Object: arbitrary properties 5027 * - `MediaError` (native): used to populate a video.js `MediaError` object 5028 * - `MediaError` (video.js): will return itself if it's already a 5029 * video.js `MediaError` object. 5030 * 5031 * @see [MediaError Spec]{@link https://dev.w3.org/html5/spec-author-view/video.html#mediaerror} 5032 * @see [Encrypted MediaError Spec]{@link https://www.w3.org/TR/2013/WD-encrypted-media-20130510/#error-codes} 5033 * 5034 * @class MediaError 5035 */ 5036function MediaError(value) { 5037 5038 // Allow redundant calls to this constructor to avoid having `instanceof` 5039 // checks peppered around the code. 5040 if (value instanceof MediaError) { 5041 return value; 5042 } 5043 5044 if (typeof value === 'number') { 5045 this.code = value; 5046 } else if (typeof value === 'string') {
vendor: 4,765 bytes, lines 5047-5211
5047 // default code is zero, so this is a custom error 5048 this.message = value; 5049 } else if (isObject(value)) { 5050 5051 // We assign the `code` property manually because native `MediaError` objects 5052 // do not expose it as an own/enumerable property of the object. 5053 if (typeof value.code === 'number') { 5054 this.code = value.code; 5055 } 5056 5057 assign(this, value); 5058 } 5059 5060 if (!this.message) { 5061 this.message = MediaError.defaultMessages[this.code] || ''; 5062 } 5063} 5064 5065/** 5066 * The error code that refers two one of the defined `MediaError` types 5067 * 5068 * @type {Number} 5069 */ 5070MediaError.prototype.code = 0; 5071 5072/** 5073 * An optional message that to show with the error. Message is not part of the HTML5 5074 * video spec but allows for more informative custom errors. 5075 * 5076 * @type {String} 5077 */ 5078MediaError.prototype.message = ''; 5079 5080/** 5081 * An optional status code that can be set by plugins to allow even more detail about 5082 * the error. For example a plugin might provide a specific HTTP status code and an 5083 * error message for that code. Then when the plugin gets that error this class will 5084 * know how to display an error message for it. This allows a custom message to show 5085 * up on the `Player` error overlay. 5086 * 5087 * @type {Array} 5088 */ 5089MediaError.prototype.status = null; 5090 5091/** 5092 * Errors indexed by the W3C standard. The order **CANNOT CHANGE**! See the 5093 * specification listed under {@link MediaError} for more information. 5094 * 5095 * @enum {array} 5096 * @readonly 5097 * @property {string} 0 - MEDIA_ERR_CUSTOM 5098 * @property {string} 1 - MEDIA_ERR_CUSTOM 5099 * @property {string} 2 - MEDIA_ERR_ABORTED 5100 * @property {string} 3 - MEDIA_ERR_NETWORK 5101 * @property {string} 4 - MEDIA_ERR_SRC_NOT_SUPPORTED 5102 * @property {string} 5 - MEDIA_ERR_ENCRYPTED 5103 */ 5104MediaError.errorTypes = ['MEDIA_ERR_CUSTOM', 'MEDIA_ERR_ABORTED', 'MEDIA_ERR_NETWORK', 'MEDIA_ERR_DECODE', 'MEDIA_ERR_SRC_NOT_SUPPORTED', 'MEDIA_ERR_ENCRYPTED']; 5105 5106/** 5107 * The default `MediaError` messages based on the {@link MediaError.errorTypes}. 5108 * 5109 * @type {Array} 5110 * @constant 5111 */ 5112MediaError.defaultMessages = { 5113 1: 'You aborted the media playback', 5114 2: 'A network error caused the media download to fail part-way.', 5115 3: 'The media playback was aborted due to a corruption problem or because the media used features your browser did not support.', 5116 4: 'The media could not be loaded, either because the server or network failed or because the format is not supported.', 5117 5: 'The media is encrypted and we do not have the keys to decrypt it.' 5118}; 5119 5120// Add types as properties on MediaError 5121// e.g. MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED = 4; 5122for (var errNum = 0; errNum < MediaError.errorTypes.length; errNum++) { 5123 MediaError[MediaError.errorTypes[errNum]] = errNum; 5124 // values should be accessible on both the class and instance 5125 MediaError.prototype[MediaError.errorTypes[errNum]] = errNum; 5126} 5127 5128var tuple = SafeParseTuple; 5129 5130function SafeParseTuple(obj, reviver) { 5131 var json; 5132 var error = null; 5133 5134 try { 5135 json = JSON.parse(obj, reviver); 5136 } catch (err) { 5137 error = err; 5138 } 5139 5140 return [error, json] 5141} 5142 5143/** 5144 * Returns whether an object is `Promise`-like (i.e. has a `then` method). 5145 * 5146 * @param {Object} value 5147 * An object that may or may not be `Promise`-like. 5148 * 5149 * @return {Boolean} 5150 * Whether or not the object is `Promise`-like. 5151 */ 5152function isPromise(value) { 5153 return value !== undefined && typeof value.then === 'function'; 5154} 5155 5156/** 5157 * Silence a Promise-like object. 5158 * 5159 * This is useful for avoiding non-harmful, but potentially confusing "uncaught 5160 * play promise" rejection error messages. 5161 * 5162 * @param {Object} value 5163 * An object that may or may not be `Promise`-like. 5164 */ 5165function silencePromise(value) { 5166 if (isPromise(value)) { 5167 value.then(null, function (e) {}); 5168 } 5169} 5170 5171/** 5172 * @file text-track-list-converter.js Utilities for capturing text track state and 5173 * re-creating tracks based on a capture. 5174 * 5175 * @module text-track-list-converter 5176 */ 5177 5178/** 5179 * Examine a single {@link TextTrack} and return a JSON-compatible javascript object that 5180 * represents the {@link TextTrack}'s state. 5181 * 5182 * @param {TextTrack} track 5183 * The text track to query. 5184 * 5185 * @return {Object} 5186 * A serializable javascript representation of the TextTrack. 5187 * @private 5188 */ 5189var trackToJson_ = function trackToJson_(track) { 5190 var ret = ['kind', 'label', 'language', 'id', 'inBandMetadataTrackDispatchType', 'mode', 'src'].reduce(function (acc, prop, i) { 5191 5192 if (track[prop]) { 5193 acc[prop] = track[prop]; 5194 } 5195 5196 return acc; 5197 }, { 5198 cues: track.cues && Array.prototype.map.call(track.cues, function (cue) { 5199 return { 5200 startTime: cue.startTime, 5201 endTime: cue.endTime, 5202 text: cue.text, 5203 id: cue.id 5204 }; 5205 }) 5206 }); 5207 5208 return ret; 5209}; 5210 5211/**
5212 * Examine a {@link Tech} and return a JSON-compatible javascript array that represents the 5213 * state of all {@link TextTrack}s currently configured. The return array is compatible with 5214 * {@link text-track-list-converter:jsonToTextTracks}. 5215 * 5216 * @param {Tech} tech 5217 * The tech object to query 5218 * 5219 * @return {Array} 5220 * A serializable javascript representation of the {@link Tech}s 5221 * {@link TextTrackList}. 5222 */ 5223var textTracksToJson = function textTracksToJson(tech) { 5224 5225 var trackEls = tech.$$('track'); 5226 5227 var trackObjs = Array.prototype.map.call(trackEls, function (t) { 5228 return t.track; 5229 }); 5230 var tracks = Array.prototype.map.call(trackEls, function (trackEl) { 5231 var json = trackToJson_(trackEl.track); 5232 5233 if (trackEl.src) { 5234 json.src = trackEl.src; 5235 } 5236 return json; 5237 }); 5238 5239 return tracks.concat(Array.prototype.filter.call(tech.textTracks(), function (track) { 5240 return trackObjs.indexOf(track) === -1; 5241 }).map(trackToJson_)); 5242}; 5243 5244/** 5245 * Create a set of remote {@link TextTrack}s on a {@link Tech} based on an array of javascript 5246 * object {@link TextTrack} representations. 5247 * 5248 * @param {Array} json 5249 * An array of `TextTrack` representation objects, like those that would be 5250 * produced by `textTracksToJson`. 5251 * 5252 * @param {Tech} tech 5253 * The `Tech` to create the `TextTrack`s on. 5254 */ 5255var jsonToTextTracks = function jsonToTextTracks(json, tech) { 5256 json.forEach(function (track) { 5257 var addedTrack = tech.addRemoteTextTrack(track).track; 5258 5259 if (!track.src && track.cues) { 5260 track.cues.forEach(function (cue) { 5261 return addedTrack.addCue(cue); 5262 }); 5263 } 5264 }); 5265 5266 return tech.textTracks(); 5267}; 5268 5269var textTrackConverter = { textTracksToJson: textTracksToJson, jsonToTextTracks: jsonToTextTracks, trackToJson_: trackToJson_ }; 5270 5271/** 5272 * @file modal-dialog.js 5273 */
vendor: 12,535 bytes, lines 5273-5732
5273 5274var MODAL_CLASS_NAME = 'vjs-modal-dialog'; 5275var ESC = 27; 5276 5277/** 5278 * The `ModalDialog` displays over the video and its controls, which blocks 5279 * interaction with the player until it is closed. 5280 * 5281 * Modal dialogs include a "Close" button and will close when that button 5282 * is activated - or when ESC is pressed anywhere. 5283 * 5284 * @extends Component 5285 */ 5286 5287var ModalDialog = function (_Component) { 5288 inherits(ModalDialog, _Component); 5289 5290 /** 5291 * Create an instance of this class. 5292 * 5293 * @param {Player} player 5294 * The `Player` that this class should be attached to. 5295 * 5296 * @param {Object} [options] 5297 * The key/value store of player options. 5298 * 5299 * @param {Mixed} [options.content=undefined] 5300 * Provide customized content for this modal. 5301 * 5302 * @param {string} [options.description] 5303 * A text description for the modal, primarily for accessibility. 5304 * 5305 * @param {boolean} [options.fillAlways=false] 5306 * Normally, modals are automatically filled only the first time 5307 * they open. This tells the modal to refresh its content 5308 * every time it opens. 5309 * 5310 * @param {string} [options.label] 5311 * A text label for the modal, primarily for accessibility. 5312 * 5313 * @param {boolean} [options.temporary=true] 5314 * If `true`, the modal can only be opened once; it will be 5315 * disposed as soon as it's closed. 5316 * 5317 * @param {boolean} [options.uncloseable=false] 5318 * If `true`, the user will not be able to close the modal 5319 * through the UI in the normal ways. Programmatic closing is 5320 * still possible. 5321 */ 5322 function ModalDialog(player, options) { 5323 classCallCheck(this, ModalDialog); 5324 5325 var _this = possibleConstructorReturn(this, _Component.call(this, player, options)); 5326 5327 _this.opened_ = _this.hasBeenOpened_ = _this.hasBeenFilled_ = false; 5328 5329 _this.closeable(!_this.options_.uncloseable); 5330 _this.content(_this.options_.content); 5331 5332 // Make sure the contentEl is defined AFTER any children are initialized 5333 // because we only want the contents of the modal in the contentEl 5334 // (not the UI elements like the close button). 5335 _this.contentEl_ = createEl('div', { 5336 className: MODAL_CLASS_NAME + '-content' 5337 }, { 5338 role: 'document' 5339 }); 5340 5341 _this.descEl_ = createEl('p', { 5342 className: MODAL_CLASS_NAME + '-description vjs-control-text', 5343 id: _this.el().getAttribute('aria-describedby') 5344 }); 5345 5346 textContent(_this.descEl_, _this.description()); 5347 _this.el_.appendChild(_this.descEl_); 5348 _this.el_.appendChild(_this.contentEl_); 5349 return _this; 5350 } 5351 5352 /** 5353 * Create the `ModalDialog`'s DOM element 5354 * 5355 * @return {Element} 5356 * The DOM element that gets created. 5357 */ 5358 5359 5360 ModalDialog.prototype.createEl = function createEl$$1() { 5361 return _Component.prototype.createEl.call(this, 'div', { 5362 className: this.buildCSSClass(), 5363 tabIndex: -1 5364 }, { 5365 'aria-describedby': this.id() + '_description', 5366 'aria-hidden': 'true', 5367 'aria-label': this.label(), 5368 'role': 'dialog' 5369 }); 5370 }; 5371 5372 ModalDialog.prototype.dispose = function dispose() { 5373 this.contentEl_ = null; 5374 this.descEl_ = null; 5375 this.previouslyActiveEl_ = null; 5376 5377 _Component.prototype.dispose.call(this); 5378 }; 5379 5380 /** 5381 * Builds the default DOM `className`. 5382 * 5383 * @return {string} 5384 * The DOM `className` for this object. 5385 */ 5386 5387 5388 ModalDialog.prototype.buildCSSClass = function buildCSSClass() { 5389 return MODAL_CLASS_NAME + ' vjs-hidden ' + _Component.prototype.buildCSSClass.call(this); 5390 }; 5391 5392 /** 5393 * Handles `keydown` events on the document, looking for ESC, which closes 5394 * the modal. 5395 * 5396 * @param {EventTarget~Event} e 5397 * The keypress that triggered this event. 5398 * 5399 * @listens keydown 5400 */ 5401 5402 5403 ModalDialog.prototype.handleKeyPress = function handleKeyPress(e) { 5404 if (e.which === ESC && this.closeable()) { 5405 this.close(); 5406 } 5407 }; 5408 5409 /** 5410 * Returns the label string for this modal. Primarily used for accessibility. 5411 * 5412 * @return {string} 5413 * the localized or raw label of this modal. 5414 */ 5415 5416 5417 ModalDialog.prototype.label = function label() { 5418 return this.localize(this.options_.label || 'Modal Window'); 5419 }; 5420 5421 /** 5422 * Returns the description string for this modal. Primarily used for 5423 * accessibility. 5424 * 5425 * @return {string} 5426 * The localized or raw description of this modal. 5427 */ 5428 5429 5430 ModalDialog.prototype.description = function description() { 5431 var desc = this.options_.description || this.localize('This is a modal window.'); 5432 5433 // Append a universal closeability message if the modal is closeable. 5434 if (this.closeable()) { 5435 desc += ' ' + this.localize('This modal can be closed by pressing the Escape key or activating the close button.'); 5436 } 5437 5438 return desc; 5439 }; 5440 5441 /** 5442 * Opens the modal. 5443 * 5444 * @fires ModalDialog#beforemodalopen 5445 * @fires ModalDialog#modalopen 5446 */ 5447 5448 5449 ModalDialog.prototype.open = function open() { 5450 if (!this.opened_) { 5451 var player = this.player(); 5452 5453 /** 5454 * Fired just before a `ModalDialog` is opened. 5455 * 5456 * @event ModalDialog#beforemodalopen 5457 * @type {EventTarget~Event} 5458 */ 5459 this.trigger('beforemodalopen'); 5460 this.opened_ = true; 5461 5462 // Fill content if the modal has never opened before and 5463 // never been filled. 5464 if (this.options_.fillAlways || !this.hasBeenOpened_ && !this.hasBeenFilled_) { 5465 this.fill(); 5466 } 5467 5468 // If the player was playing, pause it and take note of its previously 5469 // playing state. 5470 this.wasPlaying_ = !player.paused(); 5471 5472 if (this.options_.pauseOnOpen && this.wasPlaying_) { 5473 player.pause(); 5474 } 5475 5476 if (this.closeable()) { 5477 this.on(this.el_.ownerDocument, 'keydown', bind(this, this.handleKeyPress)); 5478 } 5479 5480 // Hide controls and note if they were enabled. 5481 this.hadControls_ = player.controls(); 5482 player.controls(false); 5483 5484 this.show(); 5485 this.conditionalFocus_(); 5486 this.el().setAttribute('aria-hidden', 'false'); 5487 5488 /** 5489 * Fired just after a `ModalDialog` is opened. 5490 * 5491 * @event ModalDialog#modalopen 5492 * @type {EventTarget~Event} 5493 */ 5494 this.trigger('modalopen'); 5495 this.hasBeenOpened_ = true; 5496 } 5497 }; 5498 5499 /** 5500 * If the `ModalDialog` is currently open or closed. 5501 * 5502 * @param {boolean} [value] 5503 * If given, it will open (`true`) or close (`false`) the modal. 5504 * 5505 * @return {boolean} 5506 * the current open state of the modaldialog 5507 */ 5508 5509 5510 ModalDialog.prototype.opened = function opened(value) { 5511 if (typeof value === 'boolean') { 5512 this[value ? 'open' : 'close'](); 5513 } 5514 return this.opened_; 5515 }; 5516 5517 /** 5518 * Closes the modal, does nothing if the `ModalDialog` is 5519 * not open. 5520 * 5521 * @fires ModalDialog#beforemodalclose 5522 * @fires ModalDialog#modalclose 5523 */ 5524 5525 5526 ModalDialog.prototype.close = function close() { 5527 if (!this.opened_) { 5528 return; 5529 } 5530 var player = this.player(); 5531 5532 /** 5533 * Fired just before a `ModalDialog` is closed. 5534 * 5535 * @event ModalDialog#beforemodalclose 5536 * @type {EventTarget~Event} 5537 */ 5538 this.trigger('beforemodalclose'); 5539 this.opened_ = false; 5540 5541 if (this.wasPlaying_ && this.options_.pauseOnOpen) { 5542 player.play(); 5543 } 5544 5545 if (this.closeable()) { 5546 this.off(this.el_.ownerDocument, 'keydown', bind(this, this.handleKeyPress)); 5547 } 5548 5549 if (this.hadControls_) { 5550 player.controls(true); 5551 } 5552 5553 this.hide(); 5554 this.el().setAttribute('aria-hidden', 'true'); 5555 5556 /** 5557 * Fired just after a `ModalDialog` is closed. 5558 * 5559 * @event ModalDialog#modalclose 5560 * @type {EventTarget~Event} 5561 */ 5562 this.trigger('modalclose'); 5563 this.conditionalBlur_(); 5564 5565 if (this.options_.temporary) { 5566 this.dispose(); 5567 } 5568 }; 5569 5570 /** 5571 * Check to see if the `ModalDialog` is closeable via the UI. 5572 * 5573 * @param {boolean} [value] 5574 * If given as a boolean, it will set the `closeable` option. 5575 * 5576 * @return {boolean} 5577 * Returns the final value of the closable option. 5578 */ 5579 5580 5581 ModalDialog.prototype.closeable = function closeable(value) { 5582 if (typeof value === 'boolean') { 5583 var closeable = this.closeable_ = !!value; 5584 var close = this.getChild('closeButton'); 5585 5586 // If this is being made closeable and has no close button, add one. 5587 if (closeable && !close) { 5588 5589 // The close button should be a child of the modal - not its 5590 // content element, so temporarily change the content element. 5591 var temp = this.contentEl_; 5592 5593 this.contentEl_ = this.el_; 5594 close = this.addChild('closeButton', { controlText: 'Close Modal Dialog' }); 5595 this.contentEl_ = temp; 5596 this.on(close, 'close', this.close); 5597 } 5598 5599 // If this is being made uncloseable and has a close button, remove it. 5600 if (!closeable && close) { 5601 this.off(close, 'close', this.close); 5602 this.removeChild(close); 5603 close.dispose(); 5604 } 5605 } 5606 return this.closeable_; 5607 }; 5608 5609 /** 5610 * Fill the modal's content element with the modal's "content" option. 5611 * The content element will be emptied before this change takes place. 5612 */ 5613 5614 5615 ModalDialog.prototype.fill = function fill() { 5616 this.fillWith(this.content()); 5617 }; 5618 5619 /** 5620 * Fill the modal's content element with arbitrary content. 5621 * The content element will be emptied before this change takes place. 5622 * 5623 * @fires ModalDialog#beforemodalfill 5624 * @fires ModalDialog#modalfill 5625 * 5626 * @param {Mixed} [content] 5627 * The same rules apply to this as apply to the `content` option. 5628 */ 5629 5630 5631 ModalDialog.prototype.fillWith = function fillWith(content) { 5632 var contentEl = this.contentEl(); 5633 var parentEl = contentEl.parentNode; 5634 var nextSiblingEl = contentEl.nextSibling; 5635 5636 /** 5637 * Fired just before a `ModalDialog` is filled with content. 5638 * 5639 * @event ModalDialog#beforemodalfill 5640 * @type {EventTarget~Event} 5641 */ 5642 this.trigger('beforemodalfill'); 5643 this.hasBeenFilled_ = true; 5644 5645 // Detach the content element from the DOM before performing 5646 // manipulation to avoid modifying the live DOM multiple times. 5647 parentEl.removeChild(contentEl); 5648 this.empty(); 5649 insertContent(contentEl, content); 5650 /** 5651 * Fired just after a `ModalDialog` is filled with content. 5652 * 5653 * @event ModalDialog#modalfill 5654 * @type {EventTarget~Event} 5655 */ 5656 this.trigger('modalfill'); 5657 5658 // Re-inject the re-filled content element. 5659 if (nextSiblingEl) { 5660 parentEl.insertBefore(contentEl, nextSiblingEl); 5661 } else { 5662 parentEl.appendChild(contentEl); 5663 } 5664 5665 // make sure that the close button is last in the dialog DOM 5666 var closeButton = this.getChild('closeButton'); 5667 5668 if (closeButton) { 5669 parentEl.appendChild(closeButton.el_); 5670 } 5671 }; 5672 5673 /** 5674 * Empties the content element. This happens anytime the modal is filled. 5675 * 5676 * @fires ModalDialog#beforemodalempty 5677 * @fires ModalDialog#modalempty 5678 */ 5679 5680 5681 ModalDialog.prototype.empty = function empty() { 5682 /** 5683 * Fired just before a `ModalDialog` is emptied. 5684 * 5685 * @event ModalDialog#beforemodalempty 5686 * @type {EventTarget~Event} 5687 */ 5688 this.trigger('beforemodalempty'); 5689 emptyEl(this.contentEl()); 5690 5691 /** 5692 * Fired just after a `ModalDialog` is emptied. 5693 * 5694 * @event ModalDialog#modalempty 5695 * @type {EventTarget~Event} 5696 */ 5697 this.trigger('modalempty'); 5698 }; 5699 5700 /** 5701 * Gets or sets the modal content, which gets normalized before being 5702 * rendered into the DOM. 5703 * 5704 * This does not update the DOM or fill the modal, but it is called during 5705 * that process. 5706 * 5707 * @param {Mixed} [value] 5708 * If defined, sets the internal content value to be used on the 5709 * next call(s) to `fill`. This value is normalized before being 5710 * inserted. To "clear" the internal content value, pass `null`. 5711 * 5712 * @return {Mixed} 5713 * The current content of the modal dialog 5714 */ 5715 5716 5717 ModalDialog.prototype.content = function content(value) { 5718 if (typeof value !== 'undefined') { 5719 this.content_ = value; 5720 } 5721 return this.content_; 5722 }; 5723 5724 /** 5725 * conditionally focus the modal dialog if focus was previously on the player. 5726 * 5727 * @private 5728 */ 5729 5730 5731 ModalDialog.prototype.conditionalFocus_ = function conditionalFocus_() { 5732 var activeEl =
vendor: 1,619 bytes, lines 5732-5805
5732document_1.activeElement; 5733 var playerEl = this.player_.el_; 5734 5735 this.previouslyActiveEl_ = null; 5736 5737 if (playerEl.contains(activeEl) || playerEl === activeEl) { 5738 this.previouslyActiveEl_ = activeEl; 5739 5740 this.focus(); 5741 5742 this.on(document_1, 'keydown', this.handleKeyDown); 5743 } 5744 }; 5745 5746 /** 5747 * conditionally blur the element and refocus the last focused element 5748 * 5749 * @private 5750 */ 5751 5752 5753 ModalDialog.prototype.conditionalBlur_ = function conditionalBlur_() { 5754 if (this.previouslyActiveEl_) { 5755 this.previouslyActiveEl_.focus(); 5756 this.previouslyActiveEl_ = null; 5757 } 5758 5759 this.off(document_1, 'keydown', this.handleKeyDown); 5760 }; 5761 5762 /** 5763 * Keydown handler. Attached when modal is focused. 5764 * 5765 * @listens keydown 5766 */ 5767 5768 5769 ModalDialog.prototype.handleKeyDown = function handleKeyDown(event) { 5770 // exit early if it isn't a tab key 5771 if (event.which !== 9) { 5772 return; 5773 } 5774 5775 var focusableEls = this.focusableEls_(); 5776 var activeEl = this.el_.querySelector(':focus'); 5777 var focusIndex = void 0; 5778 5779 for (var i = 0; i < focusableEls.length; i++) { 5780 if (activeEl === focusableEls[i]) { 5781 focusIndex = i; 5782 break; 5783 } 5784 } 5785 5786 if (document_1.activeElement === this.el_) { 5787 focusIndex = 0; 5788 } 5789 5790 if (event.shiftKey && focusIndex === 0) { 5791 focusableEls[focusableEls.length - 1].focus(); 5792 event.preventDefault(); 5793 } else if (!event.shiftKey && focusIndex === focusableEls.length - 1) { 5794 focusableEls[0].focus(); 5795 event.preventDefault(); 5796 } 5797 }; 5798 5799 /** 5800 * get all focusable elements 5801 * 5802 * @private 5803 */ 5804 5805
vendor: 6,242 bytes, lines 5806-6059
5806 ModalDialog.prototype.focusableEls_ = function focusableEls_() { 5807 var allChildren = this.el_.querySelectorAll('*'); 5808 5809 return Array.prototype.filter.call(allChildren, function (child) { 5810 return (child instanceof window_1.HTMLAnchorElement || child instanceof window_1.HTMLAreaElement) && child.hasAttribute('href') || (child instanceof window_1.HTMLInputElement || child instanceof window_1.HTMLSelectElement || child instanceof window_1.HTMLTextAreaElement || child instanceof window_1.HTMLButtonElement) && !child.hasAttribute('disabled') || child instanceof window_1.HTMLIFrameElement || child instanceof window_1.HTMLObjectElement || child instanceof window_1.HTMLEmbedElement || child.hasAttribute('tabindex') && child.getAttribute('tabindex') !== -1 || child.hasAttribute('contenteditable'); 5811 }); 5812 }; 5813 5814 return ModalDialog; 5815}(Component); 5816 5817/** 5818 * Default options for `ModalDialog` default options. 5819 * 5820 * @type {Object} 5821 * @private 5822 */ 5823 5824 5825ModalDialog.prototype.options_ = { 5826 pauseOnOpen: true, 5827 temporary: true 5828}; 5829 5830Component.registerComponent('ModalDialog', ModalDialog); 5831 5832/** 5833 * @file track-list.js 5834 */ 5835/** 5836 * Common functionaliy between {@link TextTrackList}, {@link AudioTrackList}, and 5837 * {@link VideoTrackList} 5838 * 5839 * @extends EventTarget 5840 */ 5841 5842var TrackList = function (_EventTarget) { 5843 inherits(TrackList, _EventTarget); 5844 5845 /** 5846 * Create an instance of this class 5847 * 5848 * @param {Track[]} tracks 5849 * A list of tracks to initialize the list with. 5850 * 5851 * @param {Object} [list] 5852 * The child object with inheritance done manually for ie8. 5853 * 5854 * @abstract 5855 */ 5856 function TrackList() { 5857 var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; 5858 5859 var _ret; 5860 5861 var list = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; 5862 classCallCheck(this, TrackList); 5863 5864 var _this = possibleConstructorReturn(this, _EventTarget.call(this)); 5865 5866 if (!list) { 5867 list = _this; // eslint-disable-line 5868 if (IS_IE8) { 5869 list = document_1.createElement('custom'); 5870 for (var prop in TrackList.prototype) { 5871 if (prop !== 'constructor') { 5872 list[prop] = TrackList.prototype[prop]; 5873 } 5874 } 5875 } 5876 } 5877 5878 list.tracks_ = []; 5879 5880 /** 5881 * @memberof TrackList 5882 * @member {number} length 5883 * The current number of `Track`s in the this Trackist. 5884 * @instance 5885 */ 5886 Object.defineProperty(list, 'length', { 5887 get: function get$$1() { 5888 return this.tracks_.length; 5889 } 5890 }); 5891 5892 for (var i = 0; i < tracks.length; i++) { 5893 list.addTrack(tracks[i]); 5894 } 5895 5896 // must return the object, as for ie8 it will not be this 5897 // but a reference to a document object 5898 return _ret = list, possibleConstructorReturn(_this, _ret); 5899 } 5900 5901 /** 5902 * Add a {@link Track} to the `TrackList` 5903 * 5904 * @param {Track} track 5905 * The audio, video, or text track to add to the list. 5906 * 5907 * @fires TrackList#addtrack 5908 */ 5909 5910 5911 TrackList.prototype.addTrack = function addTrack(track) { 5912 var index = this.tracks_.length; 5913 5914 if (!('' + index in this)) { 5915 Object.defineProperty(this, index, { 5916 get: function get$$1() { 5917 return this.tracks_[index]; 5918 } 5919 }); 5920 } 5921 5922 // Do not add duplicate tracks 5923 if (this.tracks_.indexOf(track) === -1) { 5924 this.tracks_.push(track); 5925 /** 5926 * Triggered when a track is added to a track list. 5927 * 5928 * @event TrackList#addtrack 5929 * @type {EventTarget~Event} 5930 * @property {Track} track 5931 * A reference to track that was added. 5932 */ 5933 this.trigger({ 5934 track: track, 5935 type: 'addtrack' 5936 }); 5937 } 5938 }; 5939 5940 /** 5941 * Remove a {@link Track} from the `TrackList` 5942 * 5943 * @param {Track} rtrack 5944 * The audio, video, or text track to remove from the list. 5945 * 5946 * @fires TrackList#removetrack 5947 */ 5948 5949 5950 TrackList.prototype.removeTrack = function removeTrack(rtrack) { 5951 var track = void 0; 5952 5953 for (var i = 0, l = this.length; i < l; i++) { 5954 if (this[i] === rtrack) { 5955 track = this[i]; 5956 if (track.off) { 5957 track.off(); 5958 } 5959 5960 this.tracks_.splice(i, 1); 5961 5962 break; 5963 } 5964 } 5965 5966 if (!track) { 5967 return; 5968 } 5969 5970 /** 5971 * Triggered when a track is removed from track list. 5972 * 5973 * @event TrackList#removetrack 5974 * @type {EventTarget~Event} 5975 * @property {Track} track 5976 * A reference to track that was removed. 5977 */ 5978 this.trigger({ 5979 track: track, 5980 type: 'removetrack' 5981 }); 5982 }; 5983 5984 /** 5985 * Get a Track from the TrackList by a tracks id 5986 * 5987 * @param {String} id - the id of the track to get 5988 * @method getTrackById 5989 * @return {Track} 5990 * @private 5991 */ 5992 5993 5994 TrackList.prototype.getTrackById = function getTrackById(id) { 5995 var result = null; 5996 5997 for (var i = 0, l = this.length; i < l; i++) { 5998 var track = this[i]; 5999 6000 if (track.id === id) { 6001 result = track; 6002 break; 6003 } 6004 } 6005 6006 return result; 6007 }; 6008 6009 return TrackList; 6010}(EventTarget); 6011 6012/** 6013 * Triggered when a different track is selected/enabled. 6014 * 6015 * @event TrackList#change 6016 * @type {EventTarget~Event} 6017 */ 6018 6019/** 6020 * Events that can be called with on + eventName. See {@link EventHandler}. 6021 * 6022 * @property {Object} TrackList#allowedEvents_ 6023 * @private 6024 */ 6025 6026 6027TrackList.prototype.allowedEvents_ = { 6028 change: 'change', 6029 addtrack: 'addtrack', 6030 removetrack: 'removetrack' 6031}; 6032 6033// emulate attribute EventHandler support to allow for feature detection 6034for (var event in TrackList.prototype.allowedEvents_) { 6035 TrackList.prototype['on' + event] = null; 6036} 6037 6038/** 6039 * @file audio-track-list.js 6040 */ 6041/** 6042 * Anywhere we call this function we diverge from the spec 6043 * as we only support one enabled audiotrack at a time 6044 * 6045 * @param {AudioTrackList} list 6046 * list to work on 6047 * 6048 * @param {AudioTrack} track 6049 * The track to skip 6050 * 6051 * @private 6052 */ 6053var disableOthers = function disableOthers(list, track) { 6054 for (var i = 0; i < list.length; i++) { 6055 if (!Object.keys(list[i]).length || track.id === list[i].id) { 6056 continue; 6057 } 6058 // another audio track is enabled, disable it 6059 list[i].enabled = false;
6060 } 6061}; 6062 6063/** 6064 * The current list of {@link AudioTrack} for a media file. 6065 * 6066 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist} 6067 * @extends TrackList 6068 */ 6069 6070var AudioTrackList = function (_TrackList) { 6071 inherits(AudioTrackList, _TrackList); 6072 6073 /** 6074 * Create an instance of this class. 6075 * 6076 * @param {AudioTrack[]} [tracks=[]] 6077 * A list of `AudioTrack` to instantiate the list with. 6078 */ 6079 function AudioTrackList() { 6080 var _this, _ret; 6081 6082 var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; 6083 classCallCheck(this, AudioTrackList); 6084 6085 var list = void 0; 6086 6087 // make sure only 1 track is enabled 6088 // sorted from last index to first index 6089 for (var i = tracks.length - 1; i >= 0; i--) { 6090 if (tracks[i].enabled) { 6091 disableOthers(tracks, tracks[i]); 6092 break; 6093 } 6094 } 6095 6096 // IE8 forces us to implement inheritance ourselves 6097 // as it does not support Object.defineProperty properly 6098 if (IS_IE8) { 6099 list = document_1.createElement('custom'); 6100 for (var prop in TrackList.prototype) { 6101 if (prop !== 'constructor') { 6102 list[prop] = TrackList.prototype[prop]; 6103 } 6104 } 6105 for (var _prop in AudioTrackList.prototype) { 6106 if (_prop !== 'constructor') { 6107 list[_prop] = AudioTrackList.prototype[_prop]; 6108 } 6109 } 6110 } 6111
6112 list = (_this = possibleConstructorReturn(this, _TrackList.call(this, tracks, list)), _this); 6113 list.changing_ = false; 6114 6115 return _ret = list, possibleConstructorReturn(_this, _ret); 6116 } 6117 6118 /** 6119 * Add an {@link AudioTrack} to the `AudioTrackList`. 6120 * 6121 * @param {AudioTrack} track 6122 * The AudioTrack to add to the list 6123 * 6124 * @fires TrackList#addtrack 6125 */ 6126 6127 6128 AudioTrackList.prototype.addTrack = function addTrack(track) { 6129 var _this2 = this; 6130 6131 if (track.enabled) { 6132 disableOthers(this, track); 6133 } 6134 6135 _TrackList.prototype.addTrack.call(this, track); 6136 // native tracks don't have this 6137 if (!track.addEventListener) { 6138 return; 6139 } 6140 6141 /** 6142 * @listens AudioTrack#enabledchange 6143 * @fires TrackList#change 6144 */ 6145 track.addEventListener('enabledchange', function () { 6146 // when we are disabling other tracks (since we don't support 6147 // more than one track at a time) we will set changing_ 6148 // to true so that we don't trigger additional change events 6149 if (_this2.changing_) { 6150 return; 6151 } 6152 _this2.changing_ = true; 6153 disableOthers(_this2, track); 6154 _this2.changing_ = false; 6155 _this2.trigger('change'); 6156 }); 6157 }; 6158 6159 return AudioTrackList; 6160}(TrackList); 6161 6162/** 6163 * @file video-track-list.js 6164 */ 6165/** 6166 * Un-select all other {@link VideoTrack}s that are selected. 6167 * 6168 * @param {VideoTrackList} list 6169 * list to work on 6170 * 6171 * @param {VideoTrack} track 6172 * The track to skip 6173 * 6174 * @private 6175 */ 6176var disableOthers$1 = function disableOthers(list, track) { 6177 for (var i = 0; i < list.length; i++) { 6178 if (!Object.keys(list[i]).length || track.id === list[i].id) { 6179 continue; 6180 } 6181 // another video track is enabled, disable it 6182 list[i].selected = false; 6183 } 6184}; 6185 6186/** 6187 * The current list of {@link VideoTrack} for a video. 6188 * 6189 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist} 6190 * @extends TrackList 6191 */ 6192 6193var VideoTrackList = function (_TrackList) { 6194 inherits(VideoTrackList, _TrackList); 6195 6196 /** 6197 * Create an instance of this class. 6198 * 6199 * @param {VideoTrack[]} [tracks=[]] 6200 * A list of `VideoTrack` to instantiate the list with. 6201 */ 6202 function VideoTrackList() { 6203 var _this, _ret; 6204 6205 var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; 6206 classCallCheck(this, VideoTrackList); 6207 6208 var list = void 0; 6209 6210 // make sure only 1 track is enabled 6211 // sorted from last index to first index 6212 for (var i = tracks.length - 1; i >= 0; i--) { 6213 if (tracks[i].selected) { 6214 disableOthers$1(tracks, tracks[i]); 6215 break; 6216 } 6217 } 6218 6219 // IE8 forces us to implement inheritance ourselves 6220 // as it does not support Object.defineProperty properly 6221 if (IS_IE8) { 6222 list = document_1.createElement('custom'); 6223 for (var prop in TrackList.prototype) { 6224 if (prop !== 'constructor') { 6225 list[prop] = TrackList.prototype[prop]; 6226 } 6227 } 6228 for (var _prop in VideoTrackList.prototype) { 6229 if (_prop !== 'constructor') { 6230 list[_prop] = VideoTrackList.prototype[_prop]; 6231 } 6232 } 6233 } 6234
6235 list = (_this = possibleConstructorReturn(this, _TrackList.call(this, tracks, list)), _this); 6236 list.changing_ = false; 6237 6238 /** 6239 * @member {number} VideoTrackList#selectedIndex 6240 * The current index of the selected {@link VideoTrack`}. 6241 */ 6242 Object.defineProperty(list, 'selectedIndex', { 6243 get: function get$$1() { 6244 for (var _i = 0; _i < this.length; _i++) { 6245 if (this[_i].selected) { 6246 return _i; 6247 } 6248 } 6249 return -1; 6250 }, 6251 set: function set$$1() {} 6252 }); 6253 6254 return _ret = list, possibleConstructorReturn(_this, _ret); 6255 } 6256 6257 /** 6258 * Add a {@link VideoTrack} to the `VideoTrackList`. 6259 * 6260 * @param {VideoTrack} track 6261 * The VideoTrack to add to the list 6262 * 6263 * @fires TrackList#addtrack 6264 */ 6265 6266 6267 VideoTrackList.prototype.addTrack = function addTrack(track) { 6268 var _this2 = this; 6269 6270 if (track.selected) { 6271 disableOthers$1(this, track); 6272 } 6273 6274 _TrackList.prototype.addTrack.call(this, track); 6275 // native tracks don't have this 6276 if (!track.addEventListener) { 6277 return; 6278 } 6279 6280 /** 6281 * @listens VideoTrack#selectedchange 6282 * @fires TrackList#change 6283 */ 6284 track.addEventListener('selectedchange', function () { 6285 if (_this2.changing_) { 6286 return; 6287 } 6288 _this2.changing_ = true; 6289 disableOthers$1(_this2, track); 6290 _this2.changing_ = false; 6291 _this2.trigger('change'); 6292 }); 6293 }; 6294 6295 return VideoTrackList; 6296}(TrackList); 6297 6298/** 6299 * @file text-track-list.js 6300 */ 6301/** 6302 * The current list of {@link TextTrack} for a media file. 6303 * 6304 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttracklist} 6305 * @extends TrackList 6306 */ 6307 6308var TextTrackList = function (_TrackList) { 6309 inherits(TextTrackList, _TrackList); 6310 6311 /** 6312 * Create an instance of this class. 6313 * 6314 * @param {TextTrack[]} [tracks=[]] 6315 * A list of `TextTrack` to instantiate the list with. 6316 */ 6317 function TextTrackList() { 6318 var _this, _ret; 6319 6320 var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; 6321 classCallCheck(this, TextTrackList); 6322 6323 var list = void 0; 6324 6325 // IE8 forces us to implement inheritance ourselves 6326 // as it does not support Object.defineProperty properly 6327 if (IS_IE8) { 6328 list = document_1.createElement('custom'); 6329 for (var prop in TrackList.prototype) { 6330 if (prop !== 'constructor') { 6331 list[prop] = TrackList.prototype[prop]; 6332 } 6333 } 6334 for (var _prop in TextTrackList.prototype) { 6335 if (_prop !== 'constructor') { 6336 list[_prop] = TextTrackList.prototype[_prop]; 6337 } 6338 } 6339 } 6340
vendor: 7,767 bytes, lines 6341-6675
6341 list = (_this = possibleConstructorReturn(this, _TrackList.call(this, tracks, list)), _this); 6342 return _ret = list, possibleConstructorReturn(_this, _ret); 6343 } 6344 6345 /** 6346 * Add a {@link TextTrack} to the `TextTrackList` 6347 * 6348 * @param {TextTrack} track 6349 * The text track to add to the list. 6350 * 6351 * @fires TrackList#addtrack 6352 */ 6353 6354 6355 TextTrackList.prototype.addTrack = function addTrack(track) { 6356 _TrackList.prototype.addTrack.call(this, track); 6357 6358 /** 6359 * @listens TextTrack#modechange 6360 * @fires TrackList#change 6361 */ 6362 track.addEventListener('modechange', bind(this, function () { 6363 this.trigger('change'); 6364 })); 6365 6366 var nonLanguageTextTrackKind = ['metadata', 'chapters']; 6367 6368 if (nonLanguageTextTrackKind.indexOf(track.kind) === -1) { 6369 track.addEventListener('modechange', bind(this, function () { 6370 this.trigger('selectedlanguagechange'); 6371 })); 6372 } 6373 }; 6374 6375 return TextTrackList; 6376}(TrackList); 6377 6378/** 6379 * @file html-track-element-list.js 6380 */ 6381 6382/** 6383 * The current list of {@link HtmlTrackElement}s. 6384 */ 6385 6386var HtmlTrackElementList = function () { 6387 6388 /** 6389 * Create an instance of this class. 6390 * 6391 * @param {HtmlTrackElement[]} [tracks=[]] 6392 * A list of `HtmlTrackElement` to instantiate the list with. 6393 */ 6394 function HtmlTrackElementList() { 6395 var trackElements = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; 6396 classCallCheck(this, HtmlTrackElementList); 6397 6398 var list = this; // eslint-disable-line 6399 6400 if (IS_IE8) { 6401 list = document_1.createElement('custom'); 6402 6403 for (var prop in HtmlTrackElementList.prototype) { 6404 if (prop !== 'constructor') { 6405 list[prop] = HtmlTrackElementList.prototype[prop]; 6406 } 6407 } 6408 } 6409 6410 list.trackElements_ = []; 6411 6412 /** 6413 * @memberof HtmlTrackElementList 6414 * @member {number} length 6415 * The current number of `Track`s in the this Trackist. 6416 * @instance 6417 */ 6418 Object.defineProperty(list, 'length', { 6419 get: function get$$1() { 6420 return this.trackElements_.length; 6421 } 6422 }); 6423 6424 for (var i = 0, length = trackElements.length; i < length; i++) { 6425 list.addTrackElement_(trackElements[i]); 6426 } 6427 6428 if (IS_IE8) { 6429 return list; 6430 } 6431 } 6432 6433 /** 6434 * Add an {@link HtmlTrackElement} to the `HtmlTrackElementList` 6435 * 6436 * @param {HtmlTrackElement} trackElement 6437 * The track element to add to the list. 6438 * 6439 * @private 6440 */ 6441 6442 6443 HtmlTrackElementList.prototype.addTrackElement_ = function addTrackElement_(trackElement) { 6444 var index = this.trackElements_.length; 6445 6446 if (!('' + index in this)) { 6447 Object.defineProperty(this, index, { 6448 get: function get$$1() { 6449 return this.trackElements_[index]; 6450 } 6451 }); 6452 } 6453 6454 // Do not add duplicate elements 6455 if (this.trackElements_.indexOf(trackElement) === -1) { 6456 this.trackElements_.push(trackElement); 6457 } 6458 }; 6459 6460 /** 6461 * Get an {@link HtmlTrackElement} from the `HtmlTrackElementList` given an 6462 * {@link TextTrack}. 6463 * 6464 * @param {TextTrack} track 6465 * The track associated with a track element. 6466 * 6467 * @return {HtmlTrackElement|undefined} 6468 * The track element that was found or undefined. 6469 * 6470 * @private 6471 */ 6472 6473 6474 HtmlTrackElementList.prototype.getTrackElementByTrack_ = function getTrackElementByTrack_(track) { 6475 var trackElement_ = void 0; 6476 6477 for (var i = 0, length = this.trackElements_.length; i < length; i++) { 6478 if (track === this.trackElements_[i].track) { 6479 trackElement_ = this.trackElements_[i]; 6480 6481 break; 6482 } 6483 } 6484 6485 return trackElement_; 6486 }; 6487 6488 /** 6489 * Remove a {@link HtmlTrackElement} from the `HtmlTrackElementList` 6490 * 6491 * @param {HtmlTrackElement} trackElement 6492 * The track element to remove from the list. 6493 * 6494 * @private 6495 */ 6496 6497 6498 HtmlTrackElementList.prototype.removeTrackElement_ = function removeTrackElement_(trackElement) { 6499 for (var i = 0, length = this.trackElements_.length; i < length; i++) { 6500 if (trackElement === this.trackElements_[i]) { 6501 this.trackElements_.splice(i, 1); 6502 6503 break; 6504 } 6505 } 6506 }; 6507 6508 return HtmlTrackElementList; 6509}(); 6510 6511/** 6512 * @file text-track-cue-list.js 6513 */ 6514/** 6515 * @typedef {Object} TextTrackCueList~TextTrackCue 6516 * 6517 * @property {string} id 6518 * The unique id for this text track cue 6519 * 6520 * @property {number} startTime 6521 * The start time for this text track cue 6522 * 6523 * @property {number} endTime 6524 * The end time for this text track cue 6525 * 6526 * @property {boolean} pauseOnExit 6527 * Pause when the end time is reached if true. 6528 * 6529 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcue} 6530 */ 6531 6532/** 6533 * A List of TextTrackCues. 6534 * 6535 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcuelist} 6536 */ 6537 6538var TextTrackCueList = function () { 6539 6540 /** 6541 * Create an instance of this class.. 6542 * 6543 * @param {Array} cues 6544 * A list of cues to be initialized with 6545 */ 6546 function TextTrackCueList(cues) { 6547 classCallCheck(this, TextTrackCueList); 6548 6549 var list = this; // eslint-disable-line 6550 6551 if (IS_IE8) { 6552 list = document_1.createElement('custom'); 6553 6554 for (var prop in TextTrackCueList.prototype) { 6555 if (prop !== 'constructor') { 6556 list[prop] = TextTrackCueList.prototype[prop]; 6557 } 6558 } 6559 } 6560 6561 TextTrackCueList.prototype.setCues_.call(list, cues); 6562 6563 /** 6564 * @memberof TextTrackCueList 6565 * @member {number} length 6566 * The current number of `TextTrackCue`s in the TextTrackCueList. 6567 * @instance 6568 */ 6569 Object.defineProperty(list, 'length', { 6570 get: function get$$1() { 6571 return this.length_; 6572 } 6573 }); 6574 6575 if (IS_IE8) { 6576 return list; 6577 } 6578 } 6579 6580 /** 6581 * A setter for cues in this list. Creates getters 6582 * an an index for the cues. 6583 * 6584 * @param {Array} cues 6585 * An array of cues to set 6586 * 6587 * @private 6588 */ 6589 6590 6591 TextTrackCueList.prototype.setCues_ = function setCues_(cues) { 6592 var oldLength = this.length || 0; 6593 var i = 0; 6594 var l = cues.length; 6595 6596 this.cues_ = cues; 6597 this.length_ = cues.length; 6598 6599 var defineProp = function defineProp(index) { 6600 if (!('' + index in this)) { 6601 Object.defineProperty(this, '' + index, { 6602 get: function get$$1() { 6603 return this.cues_[index]; 6604 } 6605 }); 6606 } 6607 }; 6608 6609 if (oldLength < l) { 6610 i = oldLength; 6611 6612 for (; i < l; i++) { 6613 defineProp.call(this, i); 6614 } 6615 } 6616 }; 6617 6618 /** 6619 * Get a `TextTrackCue` that is currently in the `TextTrackCueList` by id. 6620 * 6621 * @param {string} id 6622 * The id of the cue that should be searched for. 6623 * 6624 * @return {TextTrackCueList~TextTrackCue|null} 6625 * A single cue or null if none was found. 6626 */ 6627 6628 6629 TextTrackCueList.prototype.getCueById = function getCueById(id) { 6630 var result = null; 6631 6632 for (var i = 0, l = this.length; i < l; i++) { 6633 var cue = this[i]; 6634 6635 if (cue.id === id) { 6636 result = cue; 6637 break; 6638 } 6639 } 6640 6641 return result; 6642 }; 6643 6644 return TextTrackCueList; 6645}(); 6646 6647/** 6648 * @file track-kinds.js 6649 */ 6650 6651/** 6652 * All possible `VideoTrackKind`s 6653 * 6654 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-videotrack-kind 6655 * @typedef VideoTrack~Kind 6656 * @enum 6657 */ 6658var VideoTrackKind = { 6659 alternative: 'alternative', 6660 captions: 'captions', 6661 main: 'main', 6662 sign: 'sign', 6663 subtitles: 'subtitles', 6664 commentary: 'commentary' 6665}; 6666 6667/** 6668 * All possible `AudioTrackKind`s 6669 * 6670 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-audiotrack-kind 6671 * @typedef AudioTrack~Kind 6672 * @enum 6673 */ 6674var AudioTrackKind = { 6675 'alternative': 'alternative',
6676 'descriptions': 'descriptions', 6677 'main': 'main', 6678 'main-desc': 'main-desc', 6679 'translation': 'translation', 6680 'commentary': 'commentary' 6681}; 6682 6683/** 6684 * All possible `TextTrackKind`s 6685 * 6686 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-texttrack-kind 6687 * @typedef TextTrack~Kind 6688 * @enum 6689 */ 6690var TextTrackKind = { 6691 subtitles: 'subtitles', 6692 captions: 'captions', 6693 descriptions: 'descriptions', 6694 chapters: 'chapters', 6695 metadata: 'metadata' 6696}; 6697 6698/** 6699 * All possible `TextTrackMode`s 6700 * 6701 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackmode 6702 * @typedef TextTrack~Mode 6703 * @enum 6704 */ 6705var TextTrackMode = { 6706 disabled: 'disabled', 6707 hidden: 'hidden', 6708 showing: 'showing' 6709}; 6710 6711/** 6712 * @file track.js 6713 */ 6714/** 6715 * A Track class that contains all of the common functionality for {@link AudioTrack}, 6716 * {@link VideoTrack}, and {@link TextTrack}. 6717 * 6718 * > Note: This class should not be used directly 6719 * 6720 * @see {@link https://html.spec.whatwg.org/multipage/embedded-content.html} 6721 * @extends EventTarget 6722 * @abstract 6723 */ 6724 6725var Track = function (_EventTarget) { 6726 inherits(Track, _EventTarget); 6727 6728 /** 6729 * Create an instance of this class. 6730 * 6731 * @param {Object} [options={}] 6732 * Object of option names and values 6733 * 6734 * @param {string} [options.kind=''] 6735 * A valid kind for the track type you are creating. 6736 * 6737 * @param {string} [options.id='vjs_track_' + Guid.newGUID()] 6738 * A unique id for this AudioTrack. 6739 * 6740 * @param {string} [options.label=''] 6741 * The menu label for this track. 6742 * 6743 * @param {string} [options.language=''] 6744 * A valid two character language code. 6745 * 6746 * @abstract 6747 */ 6748 function Track() { 6749 var _ret; 6750 6751 var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 6752 classCallCheck(this, Track); 6753 6754 var _this = possibleConstructorReturn(this, _EventTarget.call(this)); 6755 6756 var track = _this; // eslint-disable-line 6757 6758 if (IS_IE8) { 6759 track = document_1.createElement('custom'); 6760 for (var prop in Track.prototype) { 6761 if (prop !== 'constructor') { 6762 track[prop] = Track.prototype[prop]; 6763 } 6764 } 6765 } 6766 6767 var trackProps = { 6768 id: options.id || 'vjs_track_' + newGUID(), 6769 kind: options.kind || '', 6770 label: options.label || '', 6771 language: options.language || '' 6772 }; 6773 6774 /** 6775 * @memberof Track 6776 * @member {string} id 6777 * The id of this track. Cannot be changed after creation. 6778 * @instance 6779 * 6780 * @readonly 6781 */ 6782 6783 /** 6784 * @memberof Track 6785 * @member {string} kind 6786 * The kind of track that this is. Cannot be changed after creation. 6787 * @instance 6788 * 6789 * @readonly 6790 */ 6791 6792 /** 6793 * @memberof Track 6794 * @member {string} label 6795 * The label of this track. Cannot be changed after creation. 6796 * @instance 6797 * 6798 * @readonly 6799 */ 6800 6801 /** 6802 * @memberof Track 6803 * @member {string} language 6804 * The two letter language code for this track. Cannot be changed after 6805 * creation. 6806 * @instance 6807 * 6808 * @readonly 6809 */ 6810 6811 var _loop = function _loop(key) { 6812 Object.defineProperty(track, key, { 6813 get: function get$$1() { 6814 return trackProps[key]; 6815 }, 6816 set: function set$$1() {} 6817 }); 6818 }; 6819 6820 for (var key in trackProps) { 6821 _loop(key); 6822 } 6823 6824 return _ret = track, possibleConstructorReturn(_this, _ret); 6825 } 6826 6827 return Track; 6828}(EventTarget); 6829 6830/** 6831 * @file url.js 6832 * @module url 6833 */ 6834/** 6835 * @typedef {Object} url:URLObject 6836 * 6837 * @property {string} protocol 6838 * The protocol of the url that was parsed. 6839 * 6840 * @property {string} hostname 6841 * The hostname of the url that was parsed. 6842 * 6843 * @property {string} port 6844 * The port of the url that was parsed. 6845 * 6846 * @property {string} pathname 6847 * The pathname of the url that was parsed. 6848 * 6849 * @property {string} search 6850 * The search query of the url that was parsed. 6851 * 6852 * @property {string} hash 6853 * The hash of the url that was parsed. 6854 * 6855 * @property {string} host 6856 * The host of the url that was parsed. 6857 */ 6858 6859/** 6860 * Resolve and parse the elements of a URL. 6861 * 6862 * @param {String} url 6863 * The url to parse 6864 * 6865 * @return {url:URLObject} 6866 * An object of url details 6867 */ 6868var parseUrl = function parseUrl(url) { 6869 var props = ['protocol', 'hostname', 'port', 'pathname', 'search', 'hash', 'host']; 6870 6871 // add the url to an anchor and let the browser parse the URL 6872 var a = document_1.createElement('a'); 6873 6874 a.href = url; 6875 6876 // IE8 (and 9?) Fix
vendor: 4,164 bytes, lines 6877-7031
6877 // ie8 doesn't parse the URL correctly until the anchor is actually 6878 // added to the body, and an innerHTML is needed to trigger the parsing 6879 var addToBody = a.host === '' && a.protocol !== 'file:'; 6880 var div = void 0; 6881 6882 if (addToBody) { 6883 div = document_1.createElement('div'); 6884 div.innerHTML = '<a href="' + url + '"></a>'; 6885 a = div.firstChild; 6886 // prevent the div from affecting layout 6887 div.setAttribute('style', 'display:none; position:absolute;'); 6888 document_1.body.appendChild(div); 6889 } 6890 6891 // Copy the specific URL properties to a new object 6892 // This is also needed for IE8 because the anchor loses its 6893 // properties when it's removed from the dom 6894 var details = {}; 6895 6896 for (var i = 0; i < props.length; i++) { 6897 details[props[i]] = a[props[i]]; 6898 } 6899 6900 // IE9 adds the port to the host property unlike everyone else. If 6901 // a port identifier is added for standard ports, strip it. 6902 if (details.protocol === 'http:') { 6903 details.host = details.host.replace(/:80$/, ''); 6904 } 6905 6906 if (details.protocol === 'https:') { 6907 details.host = details.host.replace(/:443$/, ''); 6908 } 6909 6910 if (!details.protocol) { 6911 details.protocol = window_1.location.protocol; 6912 } 6913 6914 if (addToBody) { 6915 document_1.body.removeChild(div); 6916 } 6917 6918 return details; 6919}; 6920 6921/** 6922 * Get absolute version of relative URL. Used to tell flash correct URL. 6923 * 6924 * 6925 * @param {string} url 6926 * URL to make absolute 6927 * 6928 * @return {string} 6929 * Absolute URL 6930 * 6931 * @see http://stackoverflow.com/questions/470832/getting-an-absolute-url-from-a-relative-one-ie6-issue 6932 */ 6933var getAbsoluteURL = function getAbsoluteURL(url) { 6934 // Check if absolute URL 6935 if (!url.match(/^https?:\/\//)) { 6936 // Convert to absolute URL. Flash hosted off-site needs an absolute URL. 6937 var div = document_1.createElement('div'); 6938 6939 div.innerHTML = '<a href="' + url + '">x</a>'; 6940 url = div.firstChild.href; 6941 } 6942 6943 return url; 6944}; 6945 6946/** 6947 * Returns the extension of the passed file name. It will return an empty string 6948 * if passed an invalid path. 6949 * 6950 * @param {string} path 6951 * The fileName path like '/path/to/file.mp4' 6952 * 6953 * @returns {string} 6954 * The extension in lower case or an empty string if no 6955 * extension could be found. 6956 */ 6957var getFileExtension = function getFileExtension(path) { 6958 if (typeof path === 'string') { 6959 var splitPathRe = /^(\/?)([\s\S]*?)((?:\.{1,2}|[^\/]+?)(\.([^\.\/\?]+)))(?:[\/]*|[\?].*)$/i; 6960 var pathParts = splitPathRe.exec(path); 6961 6962 if (pathParts) { 6963 return pathParts.pop().toLowerCase(); 6964 } 6965 } 6966 6967 return ''; 6968}; 6969 6970/** 6971 * Returns whether the url passed is a cross domain request or not. 6972 * 6973 * @param {string} url 6974 * The url to check. 6975 * 6976 * @return {boolean} 6977 * Whether it is a cross domain request or not. 6978 */ 6979var isCrossOrigin = function isCrossOrigin(url) { 6980 var winLoc = window_1.location; 6981 var urlInfo = parseUrl(url); 6982 6983 // IE8 protocol relative urls will return ':' for protocol 6984 var srcProtocol = urlInfo.protocol === ':' ? winLoc.protocol : urlInfo.protocol; 6985 6986 // Check if url is for another domain/origin 6987 // IE8 doesn't know location.origin, so we won't rely on it here 6988 var crossOrigin = srcProtocol + urlInfo.host !== winLoc.protocol + winLoc.host; 6989 6990 return crossOrigin; 6991}; 6992 6993var Url = (Object.freeze || Object)({ 6994 parseUrl: parseUrl, 6995 getAbsoluteURL: getAbsoluteURL, 6996 getFileExtension: getFileExtension, 6997 isCrossOrigin: isCrossOrigin 6998}); 6999 7000var isFunction_1 = isFunction; 7001 7002var toString$1 = Object.prototype.toString; 7003 7004function isFunction (fn) { 7005 var string = toString$1.call(fn); 7006 return string === '[object Function]' || 7007 (typeof fn === 'function' && string !== '[object RegExp]') || 7008 (typeof window !== 'undefined' && 7009 // IE8 and below 7010 (fn === window.setTimeout || 7011 fn === window.alert || 7012 fn === window.confirm || 7013 fn === window.prompt)) 7014} 7015 7016var trim_1 = createCommonjsModule(function (module, exports) { 7017exports = module.exports = trim; 7018 7019function trim(str){ 7020 return str.replace(/^\s*|\s*$/g, ''); 7021} 7022 7023exports.left = function(str){ 7024 return str.replace(/^\s*/, ''); 7025}; 7026 7027exports.right = function(str){ 7028 return str.replace(/\s*$/, ''); 7029}; 7030}); 7031
7032var forEach_1 = forEach; 7033 7034var toString$2 = Object.prototype.toString; 7035var hasOwnProperty = Object.prototype.hasOwnProperty; 7036 7037function forEach(list, iterator, context) { 7038 if (!isFunction_1(iterator)) { 7039 throw new TypeError('iterator must be a function') 7040 } 7041 7042 if (arguments.length < 3) { 7043 context = this; 7044 } 7045 7046 if (toString$2.call(list) === '[object Array]') 7047 forEachArray$1(list, iterator, context); 7048 else if (typeof list === 'string') 7049 forEachString(list, iterator, context); 7050 else 7051 forEachObject(list, iterator, context); 7052} 7053 7054function forEachArray$1(array, iterator, context) { 7055 for (var i = 0, len = array.length; i < len; i++) { 7056 if (hasOwnProperty.call(array, i)) { 7057 iterator.call(context, array[i], i, array); 7058 } 7059 } 7060} 7061 7062function forEachString(string, iterator, context) { 7063 for (var i = 0, len = string.length; i < len; i++) { 7064 // no such thing as a sparse string. 7065 iterator.call(context, string.charAt(i), i, string); 7066 } 7067} 7068 7069function forEachObject(object, iterator, context) { 7070 for (var k in object) { 7071 if (hasOwnProperty.call(object, k)) { 7072 iterator.call(context, object[k], k, object); 7073 } 7074 } 7075} 7076 7077var isArray = function(arg) { 7078 return Object.prototype.toString.call(arg) === '[object Array]'; 7079 }; 7080 7081var parseHeaders = function (headers) { 7082 if (!headers) 7083 return {} 7084 7085 var result = {}; 7086 7087 forEach_1( 7088 trim_1(headers).split('\n') 7089 , function (row) { 7090 var index = row.indexOf(':') 7091 , key = trim_1(row.slice(0, index)).toLowerCase() 7092 , value = trim_1(row.slice(index + 1)); 7093 7094 if (typeof(result[key]) === 'undefined') { 7095 result[key] = value; 7096 } else if (isArray(result[key])) { 7097 result[key].push(value); 7098 } else { 7099 result[key] = [ result[key], value ]; 7100 } 7101 } 7102 ); 7103 7104 return result 7105}; 7106 7107var immutable = extend; 7108 7109var hasOwnProperty$1 = Object.prototype.hasOwnProperty; 7110 7111function extend() { 7112 var target = {}; 7113 7114 for (var i = 0; i < arguments.length; i++) { 7115 var source = arguments[i]; 7116 7117 for (var key in source) { 7118 if (hasOwnProperty$1.call(source, key)) { 7119 target[key] = source[key]; 7120 } 7121 } 7122 } 7123 7124 return target 7125} 7126 7127var xhr = createXHR; 7128createXHR.XMLHttpRequest = window_1.XMLHttpRequest || noop; 7129createXHR.XDomainRequest = "withCredentials" in (new createXHR.XMLHttpRequest()) ? createXHR.XMLHttpRequest : window_1.XDomainRequest; 7130 7131forEachArray(["get", "put", "post", "patch", "head", "delete"], function(method) { 7132 createXHR[method === "delete" ? "del" : method] = function(uri, options, callback) { 7133 options = initParams(uri, options, callback); 7134 options.method = method.toUpperCase(); 7135 return _createXHR(options) 7136 }; 7137}); 7138 7139function forEachArray(array, iterator) { 7140 for (var i = 0; i < array.length; i++) { 7141 iterator(array[i]); 7142 } 7143} 7144 7145function isEmpty(obj){ 7146 for(var i in obj){ 7147 if(obj.hasOwnProperty(i)) return false 7148 } 7149 return true 7150} 7151 7152function initParams(uri, options, callback) { 7153 var params = uri; 7154 7155 if (isFunction_1(options)) { 7156 callback = options; 7157 if (typeof uri === "string") { 7158 params = {uri:uri}; 7159 } 7160 } else { 7161 params = immutable(options, {uri: uri}); 7162 } 7163 7164 params.callback = callback; 7165 return params 7166} 7167 7168function createXHR(uri, options, callback) { 7169 options = initParams(uri, options, callback); 7170 return _createXHR(options) 7171} 7172 7173function _createXHR(options) { 7174 if(typeof options.callback === "undefined"){ 7175 throw new Error("callback argument missing") 7176 } 7177 7178 var called = false; 7179 var callback = function cbOnce(err, response, body){ 7180 if(!called){ 7181 called = true; 7182 options.callback(err, response, body); 7183 } 7184 }; 7185 7186 function readystatechange() { 7187 if (xhr.readyState === 4) { 7188 setTimeout(loadFunc, 0); 7189 } 7190 } 7191 7192 function getBody() { 7193 // Chrome with requestType=blob throws errors arround when even testing access to responseText 7194 var body = undefined; 7195 7196 if (xhr.response) { 7197 body = xhr.response; 7198 } else { 7199 body = xhr.responseText || getXml(xhr); 7200 } 7201 7202 if (isJson) { 7203 try { 7204 body = JSON.parse(body); 7205 } catch (e) {} 7206 } 7207 7208 return body 7209 } 7210 7211 function errorFunc(evt) { 7212 clearTimeout(timeoutTimer); 7213 if(!(evt instanceof Error)){ 7214 evt = new Error("" + (evt || "Unknown XMLHttpRequest Error") ); 7215 } 7216 evt.statusCode = 0; 7217 return callback(evt, failureResponse) 7218 } 7219 7220 // will load the data & process the response in a special response object 7221 function loadFunc() { 7222 if (aborted) return 7223 var status; 7224 clearTimeout(timeoutTimer); 7225 if(options.useXDR && xhr.status===undefined) { 7226 //IE8 CORS GET successful response doesn't have a status field, but body is fine 7227 status = 200; 7228 } else { 7229 status = (xhr.status === 1223 ? 204 : xhr.status); 7230 } 7231 var response = failureResponse; 7232 var err = null; 7233 7234 if (status !== 0){ 7235 response = { 7236 body: getBody(), 7237 statusCode: status, 7238 method: method, 7239 headers: {},
vendor: 4,867 bytes, lines 7240-7400
7240 url: uri, 7241 rawRequest: xhr 7242 }; 7243 if(xhr.getAllResponseHeaders){ //remember xhr can in fact be XDR for CORS in IE 7244 response.headers = parseHeaders(xhr.getAllResponseHeaders()); 7245 } 7246 } else { 7247 err = new Error("Internal XMLHttpRequest Error"); 7248 } 7249 return callback(err, response, response.body) 7250 } 7251 7252 var xhr = options.xhr || null; 7253 7254 if (!xhr) { 7255 if (options.cors || options.useXDR) { 7256 xhr = new createXHR.XDomainRequest(); 7257 }else{ 7258 xhr = new createXHR.XMLHttpRequest(); 7259 } 7260 } 7261 7262 var key; 7263 var aborted; 7264 var uri = xhr.url = options.uri || options.url; 7265 var method = xhr.method = options.method || "GET"; 7266 var body = options.body || options.data; 7267 var headers = xhr.headers = options.headers || {}; 7268 var sync = !!options.sync; 7269 var isJson = false; 7270 var timeoutTimer; 7271 var failureResponse = { 7272 body: undefined, 7273 headers: {}, 7274 statusCode: 0, 7275 method: method, 7276 url: uri, 7277 rawRequest: xhr 7278 }; 7279 7280 if ("json" in options && options.json !== false) { 7281 isJson = true; 7282 headers["accept"] || headers["Accept"] || (headers["Accept"] = "application/json"); //Don't override existing accept header declared by user 7283 if (method !== "GET" && method !== "HEAD") { 7284 headers["content-type"] || headers["Content-Type"] || (headers["Content-Type"] = "application/json"); //Don't override existing accept header declared by user 7285 body = JSON.stringify(options.json === true ? body : options.json); 7286 } 7287 } 7288 7289 xhr.onreadystatechange = readystatechange; 7290 xhr.onload = loadFunc; 7291 xhr.onerror = errorFunc; 7292 // IE9 must have onprogress be set to a unique function. 7293 xhr.onprogress = function () { 7294 // IE must die 7295 }; 7296 xhr.onabort = function(){ 7297 aborted = true; 7298 }; 7299 xhr.ontimeout = errorFunc; 7300 xhr.open(method, uri, !sync, options.username, options.password); 7301 //has to be after open 7302 if(!sync) { 7303 xhr.withCredentials = !!options.withCredentials; 7304 } 7305 // Cannot set timeout with sync request 7306 // not setting timeout on the xhr object, because of old webkits etc. not handling that correctly 7307 // both npm's request and jquery 1.x use this kind of timeout, so this is being consistent 7308 if (!sync && options.timeout > 0 ) { 7309 timeoutTimer = setTimeout(function(){ 7310 if (aborted) return 7311 aborted = true;//IE9 may still call readystatechange 7312 xhr.abort("timeout"); 7313 var e = new Error("XMLHttpRequest timeout"); 7314 e.code = "ETIMEDOUT"; 7315 errorFunc(e); 7316 }, options.timeout ); 7317 } 7318 7319 if (xhr.setRequestHeader) { 7320 for(key in headers){ 7321 if(headers.hasOwnProperty(key)){ 7322 xhr.setRequestHeader(key, headers[key]); 7323 } 7324 } 7325 } else if (options.headers && !isEmpty(options.headers)) { 7326 throw new Error("Headers cannot be set on an XDomainRequest object") 7327 } 7328 7329 if ("responseType" in options) { 7330 xhr.responseType = options.responseType; 7331 } 7332 7333 if ("beforeSend" in options && 7334 typeof options.beforeSend === "function" 7335 ) { 7336 options.beforeSend(xhr); 7337 } 7338 7339 // Microsoft Edge browser sends "undefined" when send is called with undefined value. 7340 // XMLHttpRequest spec says to pass null as body to indicate no body 7341 // See https://github.com/naugtur/xhr/issues/100. 7342 xhr.send(body || null); 7343 7344 return xhr 7345 7346 7347} 7348 7349function getXml(xhr) { 7350 if (xhr.responseType === "document") { 7351 return xhr.responseXML 7352 } 7353 var firefoxBugTakenEffect = xhr.responseXML && xhr.responseXML.documentElement.nodeName === "parsererror"; 7354 if (xhr.responseType === "" && !firefoxBugTakenEffect) { 7355 return xhr.responseXML 7356 } 7357 7358 return null 7359} 7360 7361function noop() {} 7362 7363/** 7364 * @file text-track.js 7365 */ 7366/** 7367 * Takes a webvtt file contents and parses it into cues 7368 * 7369 * @param {string} srcContent 7370 * webVTT file contents 7371 * 7372 * @param {TextTrack} track 7373 * TextTrack to add cues to. Cues come from the srcContent. 7374 * 7375 * @private 7376 */ 7377var parseCues = function parseCues(srcContent, track) { 7378 var parser = new window_1.WebVTT.Parser(window_1, window_1.vttjs, window_1.WebVTT.StringDecoder()); 7379 var errors = []; 7380 7381 parser.oncue = function (cue) { 7382 track.addCue(cue); 7383 }; 7384 7385 parser.onparsingerror = function (error) { 7386 errors.push(error); 7387 }; 7388 7389 parser.onflush = function () { 7390 track.trigger({ 7391 type: 'loadeddata', 7392 target: track 7393 }); 7394 }; 7395 7396 parser.parse(srcContent); 7397 if (errors.length > 0) { 7398 if (window_1.console && window_1.console.groupCollapsed) { 7399 window_1.console.groupCollapsed('Text Track parsing errors for ' + track.src); 7400 }
7401 errors.forEach(function (error) { 7402 return log$1.error(error); 7403 }); 7404 if (window_1.console && window_1.console.groupEnd) { 7405 window_1.console.groupEnd(); 7406 } 7407 } 7408 7409 parser.flush(); 7410}; 7411 7412/** 7413 * Load a `TextTrack` from a specifed url. 7414 * 7415 * @param {string} src 7416 * Url to load track from. 7417 * 7418 * @param {TextTrack} track 7419 * Track to add cues to. Comes from the content at the end of `url`. 7420 * 7421 * @private 7422 */ 7423var loadTrack = function loadTrack(src, track) { 7424 var opts = { 7425 uri: src 7426 }; 7427 var crossOrigin = isCrossOrigin(src); 7428 7429 if (crossOrigin) { 7430 opts.cors = crossOrigin; 7431 } 7432 7433 xhr(opts, bind(this, function (err, response, responseBody) { 7434 if (err) { 7435 return log$1.error(err, response); 7436 } 7437 7438 track.loaded_ = true; 7439 7440 // Make sure that vttjs has loaded, otherwise, wait till it finished loading 7441 // NOTE: this is only used for the alt/video.novtt.js build 7442 if (typeof window_1.WebVTT !== 'function') { 7443 if (track.tech_) { 7444 var loadHandler = function loadHandler() { 7445 return parseCues(responseBody, track); 7446 }; 7447 7448 track.tech_.on('vttjsloaded', loadHandler); 7449 track.tech_.on('vttjserror', function () { 7450 log$1.error('vttjs failed to load, stopping trying to process ' + track.src); 7451 track.tech_.off('vttjsloaded', loadHandler); 7452 }); 7453 } 7454 } else { 7455 parseCues(responseBody, track); 7456 } 7457 })); 7458}; 7459 7460/** 7461 * A representation of a single `TextTrack`. 7462 * 7463 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrack} 7464 * @extends Track 7465 */ 7466 7467var TextTrack = function (_Track) { 7468 inherits(TextTrack, _Track); 7469 7470 /** 7471 * Create an instance of this class. 7472 * 7473 * @param {Object} options={} 7474 * Object of option names and values 7475 * 7476 * @param {Tech} options.tech 7477 * A reference to the tech that owns this TextTrack. 7478 * 7479 * @param {TextTrack~Kind} [options.kind='subtitles'] 7480 * A valid text track kind. 7481 * 7482 * @param {TextTrack~Mode} [options.mode='disabled'] 7483 * A valid text track mode. 7484 * 7485 * @param {string} [options.id='vjs_track_' + Guid.newGUID()] 7486 * A unique id for this TextTrack. 7487 * 7488 * @param {string} [options.label=''] 7489 * The menu label for this track. 7490 * 7491 * @param {string} [options.language=''] 7492 * A valid two character language code. 7493 * 7494 * @param {string} [options.srclang=''] 7495 * A valid two character language code. An alternative, but deprioritized 7496 * vesion of `options.language` 7497 * 7498 * @param {string} [options.src] 7499 * A url to TextTrack cues. 7500 * 7501 * @param {boolean} [options.default] 7502 * If this track should default to on or off. 7503 */ 7504 function TextTrack() { 7505 var _this, _ret; 7506 7507 var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 7508 classCallCheck(this, TextTrack); 7509 7510 if (!options.tech) { 7511 throw new Error('A tech was not provided.'); 7512 } 7513 7514 var settings = mergeOptions(options, { 7515 kind: TextTrackKind[options.kind] || 'subtitles', 7516 language: options.language || options.srclang || '' 7517 }); 7518 var mode = TextTrackMode[settings.mode] || 'disabled'; 7519 var default_ = settings['default']; 7520 7521 if (settings.kind === 'metadata' || settings.kind === 'chapters') { 7522 mode = 'hidden'; 7523 } 7524 // on IE8 this will be a document element 7525 // for every other browser this will be a normal object 7526 var tt = (_this = possibleConstructorReturn(this, _Track.call(this, settings)), _this); 7527 7528 tt.tech_ = settings.tech; 7529 7530 if (IS_IE8) { 7531 for (var prop in TextTrack.prototype) { 7532 if (prop !== 'constructor') { 7533 tt[prop] = TextTrack.prototype[prop]; 7534 } 7535 } 7536 } 7537 7538 tt.cues_ = []; 7539 tt.activeCues_ = []; 7540 7541 var cues = new TextTrackCueList(tt.cues_); 7542 var activeCues = new TextTrackCueList(tt.activeCues_); 7543 var changed = false; 7544 var timeupdateHandler = bind(tt, function () { 7545 7546 // Accessing this.activeCues for the side-effects of updating itself 7547 // due to it's nature as a getter function. Do not remove or cues will 7548 // stop updating! 7549 /* eslint-disable no-unused-expressions */ 7550 this.activeCues; 7551 /* eslint-enable no-unused-expressions */ 7552 if (changed) { 7553 this.trigger('cuechange'); 7554 changed = false;
vendor: 6,553 bytes, lines 7555-7810
7555 } 7556 }); 7557 7558 if (mode !== 'disabled') { 7559 tt.tech_.ready(function () { 7560 tt.tech_.on('timeupdate', timeupdateHandler); 7561 }, true); 7562 } 7563 7564 /** 7565 * @memberof TextTrack 7566 * @member {boolean} default 7567 * If this track was set to be on or off by default. Cannot be changed after 7568 * creation. 7569 * @instance 7570 * 7571 * @readonly 7572 */ 7573 Object.defineProperty(tt, 'default', { 7574 get: function get$$1() { 7575 return default_; 7576 }, 7577 set: function set$$1() {} 7578 }); 7579 7580 /** 7581 * @memberof TextTrack 7582 * @member {string} mode 7583 * Set the mode of this TextTrack to a valid {@link TextTrack~Mode}. Will 7584 * not be set if setting to an invalid mode. 7585 * @instance 7586 * 7587 * @fires TextTrack#modechange 7588 */ 7589 Object.defineProperty(tt, 'mode', { 7590 get: function get$$1() { 7591 return mode; 7592 }, 7593 set: function set$$1(newMode) { 7594 var _this2 = this; 7595 7596 if (!TextTrackMode[newMode]) { 7597 return; 7598 } 7599 mode = newMode; 7600 if (mode === 'showing') { 7601 7602 this.tech_.ready(function () { 7603 _this2.tech_.on('timeupdate', timeupdateHandler); 7604 }, true); 7605 } 7606 /** 7607 * An event that fires when mode changes on this track. This allows 7608 * the TextTrackList that holds this track to act accordingly. 7609 * 7610 * > Note: This is not part of the spec! 7611 * 7612 * @event TextTrack#modechange 7613 * @type {EventTarget~Event} 7614 */ 7615 this.trigger('modechange'); 7616 } 7617 }); 7618 7619 /** 7620 * @memberof TextTrack 7621 * @member {TextTrackCueList} cues 7622 * The text track cue list for this TextTrack. 7623 * @instance 7624 */ 7625 Object.defineProperty(tt, 'cues', { 7626 get: function get$$1() { 7627 if (!this.loaded_) { 7628 return null; 7629 } 7630 7631 return cues; 7632 }, 7633 set: function set$$1() {} 7634 }); 7635 7636 /** 7637 * @memberof TextTrack 7638 * @member {TextTrackCueList} activeCues 7639 * The list text track cues that are currently active for this TextTrack. 7640 * @instance 7641 */ 7642 Object.defineProperty(tt, 'activeCues', { 7643 get: function get$$1() { 7644 if (!this.loaded_) { 7645 return null; 7646 } 7647 7648 // nothing to do 7649 if (this.cues.length === 0) { 7650 return activeCues; 7651 } 7652 7653 var ct = this.tech_.currentTime(); 7654 var active = []; 7655 7656 for (var i = 0, l = this.cues.length; i < l; i++) { 7657 var cue = this.cues[i]; 7658 7659 if (cue.startTime <= ct && cue.endTime >= ct) { 7660 active.push(cue); 7661 } else if (cue.startTime === cue.endTime && cue.startTime <= ct && cue.startTime + 0.5 >= ct) { 7662 active.push(cue); 7663 } 7664 } 7665 7666 changed = false; 7667 7668 if (active.length !== this.activeCues_.length) { 7669 changed = true; 7670 } else { 7671 for (var _i = 0; _i < active.length; _i++) { 7672 if (this.activeCues_.indexOf(active[_i]) === -1) { 7673 changed = true; 7674 } 7675 } 7676 } 7677 7678 this.activeCues_ = active; 7679 activeCues.setCues_(this.activeCues_); 7680 7681 return activeCues; 7682 }, 7683 set: function set$$1() {} 7684 }); 7685 7686 if (settings.src) { 7687 tt.src = settings.src; 7688 loadTrack(settings.src, tt); 7689 } else { 7690 tt.loaded_ = true; 7691 } 7692 7693 return _ret = tt, possibleConstructorReturn(_this, _ret); 7694 } 7695 7696 /** 7697 * Add a cue to the internal list of cues. 7698 * 7699 * @param {TextTrack~Cue} cue 7700 * The cue to add to our internal list 7701 */ 7702 7703 7704 TextTrack.prototype.addCue = function addCue(originalCue) { 7705 var cue = originalCue; 7706 7707 if (window_1.vttjs && !(originalCue instanceof window_1.vttjs.VTTCue)) { 7708 cue = new window_1.vttjs.VTTCue(originalCue.startTime, originalCue.endTime, originalCue.text); 7709 7710 for (var prop in originalCue) { 7711 if (!(prop in cue)) { 7712 cue[prop] = originalCue[prop]; 7713 } 7714 } 7715 7716 // make sure that `id` is copied over 7717 cue.id = originalCue.id; 7718 cue.originalCue_ = originalCue; 7719 } 7720 7721 var tracks = this.tech_.textTracks(); 7722 7723 for (var i = 0; i < tracks.length; i++) { 7724 if (tracks[i] !== this) { 7725 tracks[i].removeCue(cue); 7726 } 7727 } 7728 7729 this.cues_.push(cue); 7730 this.cues.setCues_(this.cues_); 7731 }; 7732 7733 /** 7734 * Remove a cue from our internal list 7735 * 7736 * @param {TextTrack~Cue} removeCue 7737 * The cue to remove from our internal list 7738 */ 7739 7740 7741 TextTrack.prototype.removeCue = function removeCue(_removeCue) { 7742 var i = this.cues_.length; 7743 7744 while (i--) { 7745 var cue = this.cues_[i]; 7746 7747 if (cue === _removeCue || cue.originalCue_ && cue.originalCue_ === _removeCue) { 7748 this.cues_.splice(i, 1); 7749 this.cues.setCues_(this.cues_); 7750 break; 7751 } 7752 } 7753 }; 7754 7755 return TextTrack; 7756}(Track); 7757 7758/** 7759 * cuechange - One or more cues in the track have become active or stopped being active. 7760 */ 7761 7762 7763TextTrack.prototype.allowedEvents_ = { 7764 cuechange: 'cuechange' 7765}; 7766 7767/** 7768 * A representation of a single `AudioTrack`. If it is part of an {@link AudioTrackList} 7769 * only one `AudioTrack` in the list will be enabled at a time. 7770 * 7771 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotrack} 7772 * @extends Track 7773 */ 7774 7775var AudioTrack = function (_Track) { 7776 inherits(AudioTrack, _Track); 7777 7778 /** 7779 * Create an instance of this class. 7780 * 7781 * @param {Object} [options={}] 7782 * Object of option names and values 7783 * 7784 * @param {AudioTrack~Kind} [options.kind=''] 7785 * A valid audio track kind 7786 * 7787 * @param {string} [options.id='vjs_track_' + Guid.newGUID()] 7788 * A unique id for this AudioTrack. 7789 * 7790 * @param {string} [options.label=''] 7791 * The menu label for this track. 7792 * 7793 * @param {string} [options.language=''] 7794 * A valid two character language code. 7795 * 7796 * @param {boolean} [options.enabled] 7797 * If this track is the one that is currently playing. If this track is part of 7798 * an {@link AudioTrackList}, only one {@link AudioTrack} will be enabled. 7799 */ 7800 function AudioTrack() { 7801 var _this, _ret; 7802 7803 var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 7804 classCallCheck(this, AudioTrack); 7805 7806 var settings = mergeOptions(options, { 7807 kind: AudioTrackKind[options.kind] || '' 7808 }); 7809 // on IE8 this will be a document element 7810 // for every other browser this will be a normal object
7811 var track = (_this = possibleConstructorReturn(this, _Track.call(this, settings)), _this); 7812 var enabled = false; 7813 7814 if (IS_IE8) { 7815 for (var prop in AudioTrack.prototype) { 7816 if (prop !== 'constructor') { 7817 track[prop] = AudioTrack.prototype[prop]; 7818 } 7819 } 7820 } 7821 /** 7822 * @memberof AudioTrack 7823 * @member {boolean} enabled 7824 * If this `AudioTrack` is enabled or not. When setting this will 7825 * fire {@link AudioTrack#enabledchange} if the state of enabled is changed. 7826 * @instance 7827 * 7828 * @fires VideoTrack#selectedchange 7829 */ 7830 Object.defineProperty(track, 'enabled', { 7831 get: function get$$1() { 7832 return enabled; 7833 }, 7834 set: function set$$1(newEnabled) { 7835 // an invalid or unchanged value 7836 if (typeof newEnabled !== 'boolean' || newEnabled === enabled) { 7837 return; 7838 } 7839 enabled = newEnabled; 7840 7841 /** 7842 * An event that fires when enabled changes on this track. This allows 7843 * the AudioTrackList that holds this track to act accordingly. 7844 * 7845 * > Note: This is not part of the spec! Native tracks will do 7846 * this internally without an event. 7847 * 7848 * @event AudioTrack#enabledchange 7849 * @type {EventTarget~Event} 7850 */ 7851 this.trigger('enabledchange'); 7852 } 7853 }); 7854 7855 // if the user sets this track to selected then 7856 // set selected to that true value otherwise 7857 // we keep it false 7858 if (settings.enabled) { 7859 track.enabled = settings.enabled; 7860 } 7861 track.loaded_ = true; 7862 7863 return _ret = track, possibleConstructorReturn(_this, _ret); 7864 } 7865 7866 return AudioTrack; 7867}(Track); 7868 7869/** 7870 * A representation of a single `VideoTrack`. 7871 * 7872 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#videotrack} 7873 * @extends Track 7874 */ 7875 7876var VideoTrack = function (_Track) { 7877 inherits(VideoTrack, _Track); 7878 7879 /** 7880 * Create an instance of this class. 7881 * 7882 * @param {Object} [options={}] 7883 * Object of option names and values 7884 * 7885 * @param {string} [options.kind=''] 7886 * A valid {@link VideoTrack~Kind} 7887 * 7888 * @param {string} [options.id='vjs_track_' + Guid.newGUID()] 7889 * A unique id for this AudioTrack. 7890 * 7891 * @param {string} [options.label=''] 7892 * The menu label for this track. 7893 * 7894 * @param {string} [options.language=''] 7895 * A valid two character language code. 7896 * 7897 * @param {boolean} [options.selected] 7898 * If this track is the one that is currently playing. 7899 */ 7900 function VideoTrack() { 7901 var _this, _ret; 7902 7903 var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 7904 classCallCheck(this, VideoTrack); 7905 7906 var settings = mergeOptions(options, { 7907 kind: VideoTrackKind[options.kind] || '' 7908 }); 7909 7910 // on IE8 this will be a document element 7911 // for every other browser this will be a normal object 7912 var track = (_this = possibleConstructorReturn(this, _Track.call(this, settings)), _this); 7913 var selected = false; 7914 7915 if (IS_IE8) { 7916 for (var prop in VideoTrack.prototype) { 7917 if (prop !== 'constructor') { 7918 track[prop] = VideoTrack.prototype[prop]; 7919 } 7920 } 7921 } 7922 7923 /** 7924 * @memberof VideoTrack 7925 * @member {boolean} selected 7926 * If this `VideoTrack` is selected or not. When setting this will 7927 * fire {@link VideoTrack#selectedchange} if the state of selected changed. 7928 * @instance 7929 * 7930 * @fires VideoTrack#selectedchange 7931 */ 7932 Object.defineProperty(track, 'selected', { 7933 get: function get$$1() { 7934 return selected; 7935 }, 7936 set: function set$$1(newSelected) { 7937 // an invalid or unchanged value 7938 if (typeof newSelected !== 'boolean' || newSelected === selected) { 7939 return; 7940 } 7941 selected = newSelected; 7942 7943 /** 7944 * An event that fires when selected changes on this track. This allows 7945 * the VideoTrackList that holds this track to act accordingly. 7946 * 7947 * > Note: This is not part of the spec! Native tracks will do 7948 * this internally without an event. 7949 * 7950 * @event VideoTrack#selectedchange 7951 * @type {EventTarget~Event} 7952 */ 7953 this.trigger('selectedchange'); 7954 } 7955 }); 7956 7957 // if the user sets this track to selected then 7958 // set selected to that true value otherwise 7959 // we keep it false 7960 if (settings.selected) { 7961 track.selected = settings.selected; 7962 } 7963 7964 return _ret = track, possibleConstructorReturn(_this, _ret); 7965 } 7966 7967 return VideoTrack; 7968}(Track); 7969 7970/** 7971 * @file html-track-element.js 7972 */ 7973 7974/** 7975 * @memberof HTMLTrackElement 7976 * @typedef {HTMLTrackElement~ReadyState} 7977 * @enum {number} 7978 */ 7979var NONE = 0; 7980var LOADING = 1; 7981var LOADED = 2; 7982var ERROR = 3; 7983 7984/** 7985 * A single track represented in the DOM. 7986 * 7987 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#htmltrackelement}
7988 * @extends EventTarget 7989 */ 7990 7991var HTMLTrackElement = function (_EventTarget) { 7992 inherits(HTMLTrackElement, _EventTarget); 7993 7994 /** 7995 * Create an instance of this class. 7996 * 7997 * @param {Object} options={} 7998 * Object of option names and values 7999 * 8000 * @param {Tech} options.tech 8001 * A reference to the tech that owns this HTMLTrackElement. 8002 * 8003 * @param {TextTrack~Kind} [options.kind='subtitles'] 8004 * A valid text track kind. 8005 * 8006 * @param {TextTrack~Mode} [options.mode='disabled'] 8007 * A valid text track mode. 8008 * 8009 * @param {string} [options.id='vjs_track_' + Guid.newGUID()] 8010 * A unique id for this TextTrack. 8011 * 8012 * @param {string} [options.label=''] 8013 * The menu label for this track. 8014 * 8015 * @param {string} [options.language=''] 8016 * A valid two character language code. 8017 * 8018 * @param {string} [options.srclang=''] 8019 * A valid two character language code. An alternative, but deprioritized 8020 * vesion of `options.language` 8021 * 8022 * @param {string} [options.src] 8023 * A url to TextTrack cues. 8024 * 8025 * @param {boolean} [options.default] 8026 * If this track should default to on or off. 8027 */ 8028 function HTMLTrackElement() { 8029 var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 8030 classCallCheck(this, HTMLTrackElement); 8031 8032 var _this = possibleConstructorReturn(this, _EventTarget.call(this)); 8033 8034 var readyState = void 0; 8035 var trackElement = _this; // eslint-disable-line 8036 8037 if (IS_IE8) { 8038 trackElement = document_1.createElement('custom'); 8039 8040 for (var prop in HTMLTrackElement.prototype) { 8041 if (prop !== 'constructor') { 8042 trackElement[prop] = HTMLTrackElement.prototype[prop]; 8043 } 8044 } 8045 } 8046 8047 var track = new TextTrack(options); 8048 8049 trackElement.kind = track.kind; 8050 trackElement.src = track.src; 8051 trackElement.srclang = track.language; 8052 trackElement.label = track.label; 8053 trackElement['default'] = track['default']; 8054 8055 /** 8056 * @memberof HTMLTrackElement 8057 * @member {HTMLTrackElement~ReadyState} readyState 8058 * The current ready state of the track element. 8059 * @instance 8060 */ 8061 Object.defineProperty(trackElement, 'readyState', { 8062 get: function get$$1() { 8063 return readyState; 8064 } 8065 }); 8066 8067 /** 8068 * @memberof HTMLTrackElement 8069 * @member {TextTrack} track 8070 * The underlying TextTrack object. 8071 * @instance 8072 * 8073 */ 8074 Object.defineProperty(trackElement, 'track', { 8075 get: function get$$1() { 8076 return track; 8077 } 8078 }); 8079 8080 readyState = NONE; 8081 8082 /** 8083 * @listens TextTrack#loadeddata 8084 * @fires HTMLTrackElement#load 8085 */ 8086 track.addEventListener('loadeddata', function () { 8087 readyState = LOADED; 8088 8089 trackElement.trigger({ 8090 type: 'load', 8091 target: trackElement 8092 }); 8093 }); 8094 8095 if (IS_IE8) { 8096 var _ret; 8097 8098 return _ret = trackElement, possibleConstructorReturn(_this, _ret); 8099 } 8100 return _this; 8101 } 8102 8103 return HTMLTrackElement; 8104}(EventTarget); 8105 8106HTMLTrackElement.prototype.allowedEvents_ = { 8107 load: 'load' 8108}; 8109 8110HTMLTrackElement.NONE = NONE; 8111HTMLTrackElement.LOADING = LOADING; 8112HTMLTrackElement.LOADED = LOADED; 8113HTMLTrackElement.ERROR = ERROR; 8114 8115/* 8116 * This file contains all track properties that are used in 8117 * player.js, tech.js, html5.js and possibly other techs in the future. 8118 */ 8119 8120var NORMAL = { 8121 audio: { 8122 ListClass: AudioTrackList, 8123 TrackClass: AudioTrack, 8124 capitalName: 'Audio' 8125 }, 8126 video: { 8127 ListClass: VideoTrackList, 8128 TrackClass: VideoTrack, 8129 capitalName: 'Video' 8130 }, 8131 text: { 8132 ListClass: TextTrackList, 8133 TrackClass: TextTrack, 8134 capitalName: 'Text' 8135 } 8136}; 8137 8138Object.keys(NORMAL).forEach(function (type) { 8139 NORMAL[type].getterName = type + 'Tracks'; 8140 NORMAL[type].privateName = type + 'Tracks_'; 8141}); 8142 8143var REMOTE = { 8144 remoteText: { 8145 ListClass: TextTrackList, 8146 TrackClass: TextTrack, 8147 capitalName: 'RemoteText', 8148 getterName: 'remoteTextTracks', 8149 privateName: 'remoteTextTracks_' 8150 }, 8151 remoteTextEl: { 8152 ListClass: HtmlTrackElementList, 8153 TrackClass: HTMLTrackElement, 8154 capitalName: 'RemoteTextTrackEls', 8155 getterName: 'remoteTextTrackEls', 8156 privateName: 'remoteTextTrackEls_' 8157 } 8158}; 8159 8160var ALL = mergeOptions(NORMAL, REMOTE); 8161 8162REMOTE.names = Object.keys(REMOTE); 8163NORMAL.names = Object.keys(NORMAL); 8164ALL.names = [].concat(REMOTE.names).concat(NORMAL.names); 8165 8166/** 8167 * Copyright 2013 vtt.js Contributors 8168 * 8169 * Licensed under the Apache License, Version 2.0 (the "License");
vendor: 16,438 bytes, lines 8170-8707
8170 * you may not use this file except in compliance with the License. 8171 * You may obtain a copy of the License at 8172 * 8173 * http://www.apache.org/licenses/LICENSE-2.0 8174 * 8175 * Unless required by applicable law or agreed to in writing, software 8176 * distributed under the License is distributed on an "AS IS" BASIS, 8177 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8178 * See the License for the specific language governing permissions and 8179 * limitations under the License. 8180 */ 8181 8182/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 8183/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ 8184var _objCreate = Object.create || (function() { 8185 function F() {} 8186 return function(o) { 8187 if (arguments.length !== 1) { 8188 throw new Error('Object.create shim only accepts one parameter.'); 8189 } 8190 F.prototype = o; 8191 return new F(); 8192 }; 8193})(); 8194 8195// Creates a new ParserError object from an errorData object. The errorData 8196// object should have default code and message properties. The default message 8197// property can be overriden by passing in a message parameter. 8198// See ParsingError.Errors below for acceptable errors. 8199function ParsingError(errorData, message) { 8200 this.name = "ParsingError"; 8201 this.code = errorData.code; 8202 this.message = message || errorData.message; 8203} 8204ParsingError.prototype = _objCreate(Error.prototype); 8205ParsingError.prototype.constructor = ParsingError; 8206 8207// ParsingError metadata for acceptable ParsingErrors. 8208ParsingError.Errors = { 8209 BadSignature: { 8210 code: 0, 8211 message: "Malformed WebVTT signature." 8212 }, 8213 BadTimeStamp: { 8214 code: 1, 8215 message: "Malformed time stamp." 8216 } 8217}; 8218 8219// Try to parse input as a time stamp. 8220function parseTimeStamp(input) { 8221 8222 function computeSeconds(h, m, s, f) { 8223 return (h | 0) * 3600 + (m | 0) * 60 + (s | 0) + (f | 0) / 1000; 8224 } 8225 8226 var m = input.match(/^(\d+):(\d{2})(:\d{2})?\.(\d{3})/); 8227 if (!m) { 8228 return null; 8229 } 8230 8231 if (m[3]) { 8232 // Timestamp takes the form of [hours]:[minutes]:[seconds].[milliseconds] 8233 return computeSeconds(m[1], m[2], m[3].replace(":", ""), m[4]); 8234 } else if (m[1] > 59) { 8235 // Timestamp takes the form of [hours]:[minutes].[milliseconds] 8236 // First position is hours as it's over 59. 8237 return computeSeconds(m[1], m[2], 0, m[4]); 8238 } else { 8239 // Timestamp takes the form of [minutes]:[seconds].[milliseconds] 8240 return computeSeconds(0, m[1], m[2], m[4]); 8241 } 8242} 8243 8244// A settings object holds key/value pairs and will ignore anything but the first 8245// assignment to a specific key. 8246function Settings() { 8247 this.values = _objCreate(null); 8248} 8249 8250Settings.prototype = { 8251 // Only accept the first assignment to any key. 8252 set: function(k, v) { 8253 if (!this.get(k) && v !== "") { 8254 this.values[k] = v; 8255 } 8256 }, 8257 // Return the value for a key, or a default value. 8258 // If 'defaultKey' is passed then 'dflt' is assumed to be an object with 8259 // a number of possible default values as properties where 'defaultKey' is 8260 // the key of the property that will be chosen; otherwise it's assumed to be 8261 // a single value. 8262 get: function(k, dflt, defaultKey) { 8263 if (defaultKey) { 8264 return this.has(k) ? this.values[k] : dflt[defaultKey]; 8265 } 8266 return this.has(k) ? this.values[k] : dflt; 8267 }, 8268 // Check whether we have a value for a key. 8269 has: function(k) { 8270 return k in this.values; 8271 }, 8272 // Accept a setting if its one of the given alternatives. 8273 alt: function(k, v, a) { 8274 for (var n = 0; n < a.length; ++n) { 8275 if (v === a[n]) { 8276 this.set(k, v); 8277 break; 8278 } 8279 } 8280 }, 8281 // Accept a setting if its a valid (signed) integer. 8282 integer: function(k, v) { 8283 if (/^-?\d+$/.test(v)) { // integer 8284 this.set(k, parseInt(v, 10)); 8285 } 8286 }, 8287 // Accept a setting if its a valid percentage. 8288 percent: function(k, v) { 8289 var m; 8290 if ((m = v.match(/^([\d]{1,3})(\.[\d]*)?%$/))) { 8291 v = parseFloat(v); 8292 if (v >= 0 && v <= 100) { 8293 this.set(k, v); 8294 return true; 8295 } 8296 } 8297 return false; 8298 } 8299}; 8300 8301// Helper function to parse input into groups separated by 'groupDelim', and 8302// interprete each group as a key/value pair separated by 'keyValueDelim'. 8303function parseOptions(input, callback, keyValueDelim, groupDelim) { 8304 var groups = groupDelim ? input.split(groupDelim) : [input]; 8305 for (var i in groups) { 8306 if (typeof groups[i] !== "string") { 8307 continue; 8308 } 8309 var kv = groups[i].split(keyValueDelim); 8310 if (kv.length !== 2) { 8311 continue; 8312 } 8313 var k = kv[0]; 8314 var v = kv[1]; 8315 callback(k, v); 8316 } 8317} 8318 8319function parseCue(input, cue, regionList) { 8320 // Remember the original input if we need to throw an error. 8321 var oInput = input; 8322 // 4.1 WebVTT timestamp 8323 function consumeTimeStamp() { 8324 var ts = parseTimeStamp(input); 8325 if (ts === null) { 8326 throw new ParsingError(ParsingError.Errors.BadTimeStamp, 8327 "Malformed timestamp: " + oInput); 8328 } 8329 // Remove time stamp from input. 8330 input = input.replace(/^[^\sa-zA-Z-]+/, ""); 8331 return ts; 8332 } 8333 8334 // 4.4.2 WebVTT cue settings 8335 function consumeCueSettings(input, cue) { 8336 var settings = new Settings(); 8337 8338 parseOptions(input, function (k, v) { 8339 switch (k) { 8340 case "region": 8341 // Find the last region we parsed with the same region id. 8342 for (var i = regionList.length - 1; i >= 0; i--) { 8343 if (regionList[i].id === v) { 8344 settings.set(k, regionList[i].region); 8345 break; 8346 } 8347 } 8348 break; 8349 case "vertical": 8350 settings.alt(k, v, ["rl", "lr"]); 8351 break; 8352 case "line": 8353 var vals = v.split(","), 8354 vals0 = vals[0]; 8355 settings.integer(k, vals0); 8356 settings.percent(k, vals0) ? settings.set("snapToLines", false) : null; 8357 settings.alt(k, vals0, ["auto"]); 8358 if (vals.length === 2) { 8359 settings.alt("lineAlign", vals[1], ["start", "middle", "end"]); 8360 } 8361 break; 8362 case "position": 8363 vals = v.split(","); 8364 settings.percent(k, vals[0]); 8365 if (vals.length === 2) { 8366 settings.alt("positionAlign", vals[1], ["start", "middle", "end"]); 8367 } 8368 break; 8369 case "size": 8370 settings.percent(k, v); 8371 break; 8372 case "align": 8373 settings.alt(k, v, ["start", "middle", "end", "left", "right"]); 8374 break; 8375 } 8376 }, /:/, /\s/); 8377 8378 // Apply default values for any missing fields. 8379 cue.region = settings.get("region", null); 8380 cue.vertical = settings.get("vertical", ""); 8381 cue.line = settings.get("line", "auto"); 8382 cue.lineAlign = settings.get("lineAlign", "start"); 8383 cue.snapToLines = settings.get("snapToLines", true); 8384 cue.size = settings.get("size", 100); 8385 cue.align = settings.get("align", "middle"); 8386 cue.position = settings.get("position", { 8387 start: 0, 8388 left: 0, 8389 middle: 50, 8390 end: 100, 8391 right: 100 8392 }, cue.align); 8393 cue.positionAlign = settings.get("positionAlign", { 8394 start: "start", 8395 left: "start", 8396 middle: "middle", 8397 end: "end", 8398 right: "end" 8399 }, cue.align); 8400 } 8401 8402 function skipWhitespace() { 8403 input = input.replace(/^\s+/, ""); 8404 } 8405 8406 // 4.1 WebVTT cue timings. 8407 skipWhitespace(); 8408 cue.startTime = consumeTimeStamp(); // (1) collect cue start time 8409 skipWhitespace(); 8410 if (input.substr(0, 3) !== "-->") { // (3) next characters must match "-->" 8411 throw new ParsingError(ParsingError.Errors.BadTimeStamp, 8412 "Malformed time stamp (time stamps must be separated by '-->'): " + 8413 oInput); 8414 } 8415 input = input.substr(3); 8416 skipWhitespace(); 8417 cue.endTime = consumeTimeStamp(); // (5) collect cue end time 8418 8419 // 4.1 WebVTT cue settings list. 8420 skipWhitespace(); 8421 consumeCueSettings(input, cue); 8422} 8423 8424var ESCAPE = { 8425 "&": "&", 8426 "<": "<", 8427 ">": ">", 8428 "‎": "\u200e", 8429 "‏": "\u200f", 8430 " ": "\u00a0" 8431}; 8432 8433var TAG_NAME = { 8434 c: "span", 8435 i: "i", 8436 b: "b", 8437 u: "u", 8438 ruby: "ruby", 8439 rt: "rt", 8440 v: "span", 8441 lang: "span" 8442}; 8443 8444var TAG_ANNOTATION = { 8445 v: "title", 8446 lang: "lang" 8447}; 8448 8449var NEEDS_PARENT = { 8450 rt: "ruby" 8451}; 8452 8453// Parse content into a document fragment. 8454function parseContent(window, input) { 8455 function nextToken() { 8456 // Check for end-of-string. 8457 if (!input) { 8458 return null; 8459 } 8460 8461 // Consume 'n' characters from the input. 8462 function consume(result) { 8463 input = input.substr(result.length); 8464 return result; 8465 } 8466 8467 var m = input.match(/^([^<]*)(<[^>]+>?)?/); 8468 // If there is some text before the next tag, return it, otherwise return 8469 // the tag. 8470 return consume(m[1] ? m[1] : m[2]); 8471 } 8472 8473 // Unescape a string 's'. 8474 function unescape1(e) { 8475 return ESCAPE[e]; 8476 } 8477 function unescape(s) { 8478 while ((m = s.match(/&(amp|lt|gt|lrm|rlm|nbsp);/))) { 8479 s = s.replace(m[0], unescape1); 8480 } 8481 return s; 8482 } 8483 8484 function shouldAdd(current, element) { 8485 return !NEEDS_PARENT[element.localName] || 8486 NEEDS_PARENT[element.localName] === current.localName; 8487 } 8488 8489 // Create an element for this tag. 8490 function createElement(type, annotation) { 8491 var tagName = TAG_NAME[type]; 8492 if (!tagName) { 8493 return null; 8494 } 8495 var element = window.document.createElement(tagName); 8496 element.localName = tagName; 8497 var name = TAG_ANNOTATION[type]; 8498 if (name && annotation) { 8499 element[name] = annotation.trim(); 8500 } 8501 return element; 8502 } 8503 8504 var rootDiv = window.document.createElement("div"), 8505 current = rootDiv, 8506 t, 8507 tagStack = []; 8508 8509 while ((t = nextToken()) !== null) { 8510 if (t[0] === '<') { 8511 if (t[1] === "/") { 8512 // If the closing tag matches, move back up to the parent node. 8513 if (tagStack.length && 8514 tagStack[tagStack.length - 1] === t.substr(2).replace(">", "")) { 8515 tagStack.pop(); 8516 current = current.parentNode; 8517 } 8518 // Otherwise just ignore the end tag. 8519 continue; 8520 } 8521 var ts = parseTimeStamp(t.substr(1, t.length - 2)); 8522 var node; 8523 if (ts) { 8524 // Timestamps are lead nodes as well. 8525 node = window.document.createProcessingInstruction("timestamp", ts); 8526 current.appendChild(node); 8527 continue; 8528 } 8529 var m = t.match(/^<([^.\s/0-9>]+)(\.[^\s\\>]+)?([^>\\]+)?(\\?)>?$/); 8530 // If we can't parse the tag, skip to the next tag. 8531 if (!m) { 8532 continue; 8533 } 8534 // Try to construct an element, and ignore the tag if we couldn't. 8535 node = createElement(m[1], m[3]); 8536 if (!node) { 8537 continue; 8538 } 8539 // Determine if the tag should be added based on the context of where it 8540 // is placed in the cuetext. 8541 if (!shouldAdd(current, node)) { 8542 continue; 8543 } 8544 // Set the class list (as a list of classes, separated by space). 8545 if (m[2]) { 8546 node.className = m[2].substr(1).replace('.', ' '); 8547 } 8548 // Append the node to the current node, and enter the scope of the new 8549 // node. 8550 tagStack.push(m[1]); 8551 current.appendChild(node); 8552 current = node; 8553 continue; 8554 } 8555 8556 // Text nodes are leaf nodes. 8557 current.appendChild(window.document.createTextNode(unescape(t))); 8558 } 8559 8560 return rootDiv; 8561} 8562 8563// This is a list of all the Unicode characters that have a strong 8564// right-to-left category. What this means is that these characters are 8565// written right-to-left for sure. It was generated by pulling all the strong 8566// right-to-left characters out of the Unicode data table. That table can 8567// found at: http://www.unicode.org/Public/UNIDATA/UnicodeData.txt 8568var strongRTLRanges = [[0x5be, 0x5be], [0x5c0, 0x5c0], [0x5c3, 0x5c3], [0x5c6, 0x5c6], 8569 [0x5d0, 0x5ea], [0x5f0, 0x5f4], [0x608, 0x608], [0x60b, 0x60b], [0x60d, 0x60d], 8570 [0x61b, 0x61b], [0x61e, 0x64a], [0x66d, 0x66f], [0x671, 0x6d5], [0x6e5, 0x6e6], 8571 [0x6ee, 0x6ef], [0x6fa, 0x70d], [0x70f, 0x710], [0x712, 0x72f], [0x74d, 0x7a5], 8572 [0x7b1, 0x7b1], [0x7c0, 0x7ea], [0x7f4, 0x7f5], [0x7fa, 0x7fa], [0x800, 0x815], 8573 [0x81a, 0x81a], [0x824, 0x824], [0x828, 0x828], [0x830, 0x83e], [0x840, 0x858], 8574 [0x85e, 0x85e], [0x8a0, 0x8a0], [0x8a2, 0x8ac], [0x200f, 0x200f], 8575 [0xfb1d, 0xfb1d], [0xfb1f, 0xfb28], [0xfb2a, 0xfb36], [0xfb38, 0xfb3c], 8576 [0xfb3e, 0xfb3e], [0xfb40, 0xfb41], [0xfb43, 0xfb44], [0xfb46, 0xfbc1], 8577 [0xfbd3, 0xfd3d], [0xfd50, 0xfd8f], [0xfd92, 0xfdc7], [0xfdf0, 0xfdfc], 8578 [0xfe70, 0xfe74], [0xfe76, 0xfefc], [0x10800, 0x10805], [0x10808, 0x10808], 8579 [0x1080a, 0x10835], [0x10837, 0x10838], [0x1083c, 0x1083c], [0x1083f, 0x10855], 8580 [0x10857, 0x1085f], [0x10900, 0x1091b], [0x10920, 0x10939], [0x1093f, 0x1093f], 8581 [0x10980, 0x109b7], [0x109be, 0x109bf], [0x10a00, 0x10a00], [0x10a10, 0x10a13], 8582 [0x10a15, 0x10a17], [0x10a19, 0x10a33], [0x10a40, 0x10a47], [0x10a50, 0x10a58], 8583 [0x10a60, 0x10a7f], [0x10b00, 0x10b35], [0x10b40, 0x10b55], [0x10b58, 0x10b72], 8584 [0x10b78, 0x10b7f], [0x10c00, 0x10c48], [0x1ee00, 0x1ee03], [0x1ee05, 0x1ee1f], 8585 [0x1ee21, 0x1ee22], [0x1ee24, 0x1ee24], [0x1ee27, 0x1ee27], [0x1ee29, 0x1ee32], 8586 [0x1ee34, 0x1ee37], [0x1ee39, 0x1ee39], [0x1ee3b, 0x1ee3b], [0x1ee42, 0x1ee42], 8587 [0x1ee47, 0x1ee47], [0x1ee49, 0x1ee49], [0x1ee4b, 0x1ee4b], [0x1ee4d, 0x1ee4f], 8588 [0x1ee51, 0x1ee52], [0x1ee54, 0x1ee54], [0x1ee57, 0x1ee57], [0x1ee59, 0x1ee59], 8589 [0x1ee5b, 0x1ee5b], [0x1ee5d, 0x1ee5d], [0x1ee5f, 0x1ee5f], [0x1ee61, 0x1ee62], 8590 [0x1ee64, 0x1ee64], [0x1ee67, 0x1ee6a], [0x1ee6c, 0x1ee72], [0x1ee74, 0x1ee77], 8591 [0x1ee79, 0x1ee7c], [0x1ee7e, 0x1ee7e], [0x1ee80, 0x1ee89], [0x1ee8b, 0x1ee9b], 8592 [0x1eea1, 0x1eea3], [0x1eea5, 0x1eea9], [0x1eeab, 0x1eebb], [0x10fffd, 0x10fffd]]; 8593 8594function isStrongRTLChar(charCode) { 8595 for (var i = 0; i < strongRTLRanges.length; i++) { 8596 var currentRange = strongRTLRanges[i]; 8597 if (charCode >= currentRange[0] && charCode <= currentRange[1]) { 8598 return true; 8599 } 8600 } 8601 8602 return false; 8603} 8604 8605function determineBidi(cueDiv) { 8606 var nodeStack = [], 8607 text = "", 8608 charCode; 8609 8610 if (!cueDiv || !cueDiv.childNodes) { 8611 return "ltr"; 8612 } 8613 8614 function pushNodes(nodeStack, node) { 8615 for (var i = node.childNodes.length - 1; i >= 0; i--) { 8616 nodeStack.push(node.childNodes[i]); 8617 } 8618 } 8619 8620 function nextTextNode(nodeStack) { 8621 if (!nodeStack || !nodeStack.length) { 8622 return null; 8623 } 8624 8625 var node = nodeStack.pop(), 8626 text = node.textContent || node.innerText; 8627 if (text) { 8628 // TODO: This should match all unicode type B characters (paragraph 8629 // separator characters). See issue #115. 8630 var m = text.match(/^.*(\n|\r)/); 8631 if (m) { 8632 nodeStack.length = 0; 8633 return m[0]; 8634 } 8635 return text; 8636 } 8637 if (node.tagName === "ruby") { 8638 return nextTextNode(nodeStack); 8639 } 8640 if (node.childNodes) { 8641 pushNodes(nodeStack, node); 8642 return nextTextNode(nodeStack); 8643 } 8644 } 8645 8646 pushNodes(nodeStack, cueDiv); 8647 while ((text = nextTextNode(nodeStack))) { 8648 for (var i = 0; i < text.length; i++) { 8649 charCode = text.charCodeAt(i); 8650 if (isStrongRTLChar(charCode)) { 8651 return "rtl"; 8652 } 8653 } 8654 } 8655 return "ltr"; 8656} 8657 8658function computeLinePos(cue) { 8659 if (typeof cue.line === "number" && 8660 (cue.snapToLines || (cue.line >= 0 && cue.line <= 100))) { 8661 return cue.line; 8662 } 8663 if (!cue.track || !cue.track.textTrackList || 8664 !cue.track.textTrackList.mediaElement) { 8665 return -1; 8666 } 8667 var track = cue.track, 8668 trackList = track.textTrackList, 8669 count = 0; 8670 for (var i = 0; i < trackList.length && trackList[i] !== track; i++) { 8671 if (trackList[i].mode === "showing") { 8672 count++; 8673 } 8674 } 8675 return ++count * -1; 8676} 8677 8678function StyleBox() { 8679} 8680 8681// Apply styles to a div. If there is no div passed then it defaults to the 8682// div on 'this'. 8683StyleBox.prototype.applyStyles = function(styles, div) { 8684 div = div || this.div; 8685 for (var prop in styles) { 8686 if (styles.hasOwnProperty(prop)) { 8687 div.style[prop] = styles[prop]; 8688 } 8689 } 8690}; 8691 8692StyleBox.prototype.formatStyle = function(val, unit) { 8693 return val === 0 ? 0 : val + unit; 8694}; 8695 8696// Constructs the computed display state of the cue (a div). Places the div 8697// into the overlay which should be a block level element (usually a div). 8698function CueStyleBox(window, cue, styleOptions) { 8699 var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent); 8700 var color = "rgba(255, 255, 255, 1)"; 8701 var backgroundColor = "rgba(0, 0, 0, 0.8)"; 8702 8703 if (isIE8) { 8704 color = "rgb(255, 255, 255)"; 8705 backgroundColor = "rgb(0, 0, 0)"; 8706 } 8707
vendor: 5,880 bytes, lines 8708-8884
8708 StyleBox.call(this); 8709 this.cue = cue; 8710 8711 // Parse our cue's text into a DOM tree rooted at 'cueDiv'. This div will 8712 // have inline positioning and will function as the cue background box. 8713 this.cueDiv = parseContent(window, cue.text); 8714 var styles = { 8715 color: color, 8716 backgroundColor: backgroundColor, 8717 position: "relative", 8718 left: 0, 8719 right: 0, 8720 top: 0, 8721 bottom: 0, 8722 display: "inline" 8723 }; 8724 8725 if (!isIE8) { 8726 styles.writingMode = cue.vertical === "" ? "horizontal-tb" 8727 : cue.vertical === "lr" ? "vertical-lr" 8728 : "vertical-rl"; 8729 styles.unicodeBidi = "plaintext"; 8730 } 8731 this.applyStyles(styles, this.cueDiv); 8732 8733 // Create an absolutely positioned div that will be used to position the cue 8734 // div. Note, all WebVTT cue-setting alignments are equivalent to the CSS 8735 // mirrors of them except "middle" which is "center" in CSS. 8736 this.div = window.document.createElement("div"); 8737 styles = { 8738 textAlign: cue.align === "middle" ? "center" : cue.align, 8739 font: styleOptions.font, 8740 whiteSpace: "pre-line", 8741 position: "absolute" 8742 }; 8743 8744 if (!isIE8) { 8745 styles.direction = determineBidi(this.cueDiv); 8746 styles.writingMode = cue.vertical === "" ? "horizontal-tb" 8747 : cue.vertical === "lr" ? "vertical-lr" 8748 : "vertical-rl". 8749 stylesunicodeBidi = "plaintext"; 8750 } 8751 8752 this.applyStyles(styles); 8753 8754 this.div.appendChild(this.cueDiv); 8755 8756 // Calculate the distance from the reference edge of the viewport to the text 8757 // position of the cue box. The reference edge will be resolved later when 8758 // the box orientation styles are applied. 8759 var textPos = 0; 8760 switch (cue.positionAlign) { 8761 case "start": 8762 textPos = cue.position; 8763 break; 8764 case "middle": 8765 textPos = cue.position - (cue.size / 2); 8766 break; 8767 case "end": 8768 textPos = cue.position - cue.size; 8769 break; 8770 } 8771 8772 // Horizontal box orientation; textPos is the distance from the left edge of the 8773 // area to the left edge of the box and cue.size is the distance extending to 8774 // the right from there. 8775 if (cue.vertical === "") { 8776 this.applyStyles({ 8777 left: this.formatStyle(textPos, "%"), 8778 width: this.formatStyle(cue.size, "%") 8779 }); 8780 // Vertical box orientation; textPos is the distance from the top edge of the 8781 // area to the top edge of the box and cue.size is the height extending 8782 // downwards from there. 8783 } else { 8784 this.applyStyles({ 8785 top: this.formatStyle(textPos, "%"), 8786 height: this.formatStyle(cue.size, "%") 8787 }); 8788 } 8789 8790 this.move = function(box) { 8791 this.applyStyles({ 8792 top: this.formatStyle(box.top, "px"), 8793 bottom: this.formatStyle(box.bottom, "px"), 8794 left: this.formatStyle(box.left, "px"), 8795 right: this.formatStyle(box.right, "px"), 8796 height: this.formatStyle(box.height, "px"), 8797 width: this.formatStyle(box.width, "px") 8798 }); 8799 }; 8800} 8801CueStyleBox.prototype = _objCreate(StyleBox.prototype); 8802CueStyleBox.prototype.constructor = CueStyleBox; 8803 8804// Represents the co-ordinates of an Element in a way that we can easily 8805// compute things with such as if it overlaps or intersects with another Element. 8806// Can initialize it with either a StyleBox or another BoxPosition. 8807function BoxPosition(obj) { 8808 var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent); 8809 8810 // Either a BoxPosition was passed in and we need to copy it, or a StyleBox 8811 // was passed in and we need to copy the results of 'getBoundingClientRect' 8812 // as the object returned is readonly. All co-ordinate values are in reference 8813 // to the viewport origin (top left). 8814 var lh, height, width, top; 8815 if (obj.div) { 8816 height = obj.div.offsetHeight; 8817 width = obj.div.offsetWidth; 8818 top = obj.div.offsetTop; 8819 8820 var rects = (rects = obj.div.childNodes) && (rects = rects[0]) && 8821 rects.getClientRects && rects.getClientRects(); 8822 obj = obj.div.getBoundingClientRect(); 8823 // In certain cases the outter div will be slightly larger then the sum of 8824 // the inner div's lines. This could be due to bold text, etc, on some platforms. 8825 // In this case we should get the average line height and use that. This will 8826 // result in the desired behaviour. 8827 lh = rects ? Math.max((rects[0] && rects[0].height) || 0, obj.height / rects.length) 8828 : 0; 8829 8830 } 8831 this.left = obj.left; 8832 this.right = obj.right; 8833 this.top = obj.top || top; 8834 this.height = obj.height || height; 8835 this.bottom = obj.bottom || (top + (obj.height || height)); 8836 this.width = obj.width || width; 8837 this.lineHeight = lh !== undefined ? lh : obj.lineHeight; 8838 8839 if (isIE8 && !this.lineHeight) { 8840 this.lineHeight = 13; 8841 } 8842} 8843 8844// Move the box along a particular axis. Optionally pass in an amount to move 8845// the box. If no amount is passed then the default is the line height of the 8846// box. 8847BoxPosition.prototype.move = function(axis, toMove) { 8848 toMove = toMove !== undefined ? toMove : this.lineHeight; 8849 switch (axis) { 8850 case "+x": 8851 this.left += toMove; 8852 this.right += toMove; 8853 break; 8854 case "-x": 8855 this.left -= toMove; 8856 this.right -= toMove; 8857 break; 8858 case "+y": 8859 this.top += toMove; 8860 this.bottom += toMove; 8861 break; 8862 case "-y": 8863 this.top -= toMove; 8864 this.bottom -= toMove; 8865 break; 8866 } 8867}; 8868 8869// Check if this box overlaps another box, b2. 8870BoxPosition.prototype.overlaps = function(b2) { 8871 return this.left < b2.right && 8872 this.right > b2.left && 8873 this.top < b2.bottom && 8874 this.bottom > b2.top; 8875}; 8876 8877// Check if this box overlaps any other boxes in boxes. 8878BoxPosition.prototype.overlapsAny = function(boxes) { 8879 for (var i = 0; i < boxes.length; i++) { 8880 if (this.overlaps(boxes[i])) { 8881 return true; 8882 } 8883 } 8884 return false;
vendor: 6,016 bytes, lines 8885-9048
8885}; 8886 8887// Check if this box is within another box. 8888BoxPosition.prototype.within = function(container) { 8889 return this.top >= container.top && 8890 this.bottom <= container.bottom && 8891 this.left >= container.left && 8892 this.right <= container.right; 8893}; 8894 8895// Check if this box is entirely within the container or it is overlapping 8896// on the edge opposite of the axis direction passed. For example, if "+x" is 8897// passed and the box is overlapping on the left edge of the container, then 8898// return true. 8899BoxPosition.prototype.overlapsOppositeAxis = function(container, axis) { 8900 switch (axis) { 8901 case "+x": 8902 return this.left < container.left; 8903 case "-x": 8904 return this.right > container.right; 8905 case "+y": 8906 return this.top < container.top; 8907 case "-y": 8908 return this.bottom > container.bottom; 8909 } 8910}; 8911 8912// Find the percentage of the area that this box is overlapping with another 8913// box. 8914BoxPosition.prototype.intersectPercentage = function(b2) { 8915 var x = Math.max(0, Math.min(this.right, b2.right) - Math.max(this.left, b2.left)), 8916 y = Math.max(0, Math.min(this.bottom, b2.bottom) - Math.max(this.top, b2.top)), 8917 intersectArea = x * y; 8918 return intersectArea / (this.height * this.width); 8919}; 8920 8921// Convert the positions from this box to CSS compatible positions using 8922// the reference container's positions. This has to be done because this 8923// box's positions are in reference to the viewport origin, whereas, CSS 8924// values are in referecne to their respective edges. 8925BoxPosition.prototype.toCSSCompatValues = function(reference) { 8926 return { 8927 top: this.top - reference.top, 8928 bottom: reference.bottom - this.bottom, 8929 left: this.left - reference.left, 8930 right: reference.right - this.right, 8931 height: this.height, 8932 width: this.width 8933 }; 8934}; 8935 8936// Get an object that represents the box's position without anything extra. 8937// Can pass a StyleBox, HTMLElement, or another BoxPositon. 8938BoxPosition.getSimpleBoxPosition = function(obj) { 8939 var height = obj.div ? obj.div.offsetHeight : obj.tagName ? obj.offsetHeight : 0; 8940 var width = obj.div ? obj.div.offsetWidth : obj.tagName ? obj.offsetWidth : 0; 8941 var top = obj.div ? obj.div.offsetTop : obj.tagName ? obj.offsetTop : 0; 8942 8943 obj = obj.div ? obj.div.getBoundingClientRect() : 8944 obj.tagName ? obj.getBoundingClientRect() : obj; 8945 var ret = { 8946 left: obj.left, 8947 right: obj.right, 8948 top: obj.top || top, 8949 height: obj.height || height, 8950 bottom: obj.bottom || (top + (obj.height || height)), 8951 width: obj.width || width 8952 }; 8953 return ret; 8954}; 8955 8956// Move a StyleBox to its specified, or next best, position. The containerBox 8957// is the box that contains the StyleBox, such as a div. boxPositions are 8958// a list of other boxes that the styleBox can't overlap with. 8959function moveBoxToLinePosition(window, styleBox, containerBox, boxPositions) { 8960 8961 // Find the best position for a cue box, b, on the video. The axis parameter 8962 // is a list of axis, the order of which, it will move the box along. For example: 8963 // Passing ["+x", "-x"] will move the box first along the x axis in the positive 8964 // direction. If it doesn't find a good position for it there it will then move 8965 // it along the x axis in the negative direction. 8966 function findBestPosition(b, axis) { 8967 var bestPosition, 8968 specifiedPosition = new BoxPosition(b), 8969 percentage = 1; // Highest possible so the first thing we get is better. 8970 8971 for (var i = 0; i < axis.length; i++) { 8972 while (b.overlapsOppositeAxis(containerBox, axis[i]) || 8973 (b.within(containerBox) && b.overlapsAny(boxPositions))) { 8974 b.move(axis[i]); 8975 } 8976 // We found a spot where we aren't overlapping anything. This is our 8977 // best position. 8978 if (b.within(containerBox)) { 8979 return b; 8980 } 8981 var p = b.intersectPercentage(containerBox); 8982 // If we're outside the container box less then we were on our last try 8983 // then remember this position as the best position. 8984 if (percentage > p) { 8985 bestPosition = new BoxPosition(b); 8986 percentage = p; 8987 } 8988 // Reset the box position to the specified position. 8989 b = new BoxPosition(specifiedPosition); 8990 } 8991 return bestPosition || specifiedPosition; 8992 } 8993 8994 var boxPosition = new BoxPosition(styleBox), 8995 cue = styleBox.cue, 8996 linePos = computeLinePos(cue), 8997 axis = []; 8998 8999 // If we have a line number to align the cue to. 9000 if (cue.snapToLines) { 9001 var size; 9002 switch (cue.vertical) { 9003 case "": 9004 axis = [ "+y", "-y" ]; 9005 size = "height"; 9006 break; 9007 case "rl": 9008 axis = [ "+x", "-x" ]; 9009 size = "width"; 9010 break; 9011 case "lr": 9012 axis = [ "-x", "+x" ]; 9013 size = "width"; 9014 break; 9015 } 9016 9017 var step = boxPosition.lineHeight, 9018 position = step * Math.round(linePos), 9019 maxPosition = containerBox[size] + step, 9020 initialAxis = axis[0]; 9021 9022 // If the specified intial position is greater then the max position then 9023 // clamp the box to the amount of steps it would take for the box to 9024 // reach the max position. 9025 if (Math.abs(position) > maxPosition) { 9026 position = position < 0 ? -1 : 1; 9027 position *= Math.ceil(maxPosition / step) * step; 9028 } 9029 9030 // If computed line position returns negative then line numbers are 9031 // relative to the bottom of the video instead of the top. Therefore, we 9032 // need to increase our initial position by the length or width of the 9033 // video, depending on the writing direction, and reverse our axis directions. 9034 if (linePos < 0) { 9035 position += cue.vertical === "" ? containerBox.height : containerBox.width; 9036 axis = axis.reverse(); 9037 } 9038 9039 // Move the box to the specified position. This may not be its best 9040 // position. 9041 boxPosition.move(initialAxis, position); 9042 9043 } else { 9044 // If we have a percentage line value for the cue. 9045 var calculatedPercentage = (boxPosition.lineHeight / containerBox.height) * 100; 9046 9047 switch (cue.lineAlign) { 9048 case "middle
vendor: 10,353 bytes, lines 9048-9409
9048": 9049 linePos -= (calculatedPercentage / 2); 9050 break; 9051 case "end": 9052 linePos -= calculatedPercentage; 9053 break; 9054 } 9055 9056 // Apply initial line position to the cue box. 9057 switch (cue.vertical) { 9058 case "": 9059 styleBox.applyStyles({ 9060 top: styleBox.formatStyle(linePos, "%") 9061 }); 9062 break; 9063 case "rl": 9064 styleBox.applyStyles({ 9065 left: styleBox.formatStyle(linePos, "%") 9066 }); 9067 break; 9068 case "lr": 9069 styleBox.applyStyles({ 9070 right: styleBox.formatStyle(linePos, "%") 9071 }); 9072 break; 9073 } 9074 9075 axis = [ "+y", "-x", "+x", "-y" ]; 9076 9077 // Get the box position again after we've applied the specified positioning 9078 // to it. 9079 boxPosition = new BoxPosition(styleBox); 9080 } 9081 9082 var bestPosition = findBestPosition(boxPosition, axis); 9083 styleBox.move(bestPosition.toCSSCompatValues(containerBox)); 9084} 9085 9086function WebVTT$1() { 9087 // Nothing 9088} 9089 9090// Helper to allow strings to be decoded instead of the default binary utf8 data. 9091WebVTT$1.StringDecoder = function() { 9092 return { 9093 decode: function(data) { 9094 if (!data) { 9095 return ""; 9096 } 9097 if (typeof data !== "string") { 9098 throw new Error("Error - expected string data."); 9099 } 9100 return decodeURIComponent(encodeURIComponent(data)); 9101 } 9102 }; 9103}; 9104 9105WebVTT$1.convertCueToDOMTree = function(window, cuetext) { 9106 if (!window || !cuetext) { 9107 return null; 9108 } 9109 return parseContent(window, cuetext); 9110}; 9111 9112var FONT_SIZE_PERCENT = 0.05; 9113var FONT_STYLE = "sans-serif"; 9114var CUE_BACKGROUND_PADDING = "1.5%"; 9115 9116// Runs the processing model over the cues and regions passed to it. 9117// @param overlay A block level element (usually a div) that the computed cues 9118// and regions will be placed into. 9119WebVTT$1.processCues = function(window, cues, overlay) { 9120 if (!window || !cues || !overlay) { 9121 return null; 9122 } 9123 9124 // Remove all previous children. 9125 while (overlay.firstChild) { 9126 overlay.removeChild(overlay.firstChild); 9127 } 9128 9129 var paddedOverlay = window.document.createElement("div"); 9130 paddedOverlay.style.position = "absolute"; 9131 paddedOverlay.style.left = "0"; 9132 paddedOverlay.style.right = "0"; 9133 paddedOverlay.style.top = "0"; 9134 paddedOverlay.style.bottom = "0"; 9135 paddedOverlay.style.margin = CUE_BACKGROUND_PADDING; 9136 overlay.appendChild(paddedOverlay); 9137 9138 // Determine if we need to compute the display states of the cues. This could 9139 // be the case if a cue's state has been changed since the last computation or 9140 // if it has not been computed yet. 9141 function shouldCompute(cues) { 9142 for (var i = 0; i < cues.length; i++) { 9143 if (cues[i].hasBeenReset || !cues[i].displayState) { 9144 return true; 9145 } 9146 } 9147 return false; 9148 } 9149 9150 // We don't need to recompute the cues' display states. Just reuse them. 9151 if (!shouldCompute(cues)) { 9152 for (var i = 0; i < cues.length; i++) { 9153 paddedOverlay.appendChild(cues[i].displayState); 9154 } 9155 return; 9156 } 9157 9158 var boxPositions = [], 9159 containerBox = BoxPosition.getSimpleBoxPosition(paddedOverlay), 9160 fontSize = Math.round(containerBox.height * FONT_SIZE_PERCENT * 100) / 100; 9161 var styleOptions = { 9162 font: fontSize + "px " + FONT_STYLE 9163 }; 9164 9165 (function() { 9166 var styleBox, cue; 9167 9168 for (var i = 0; i < cues.length; i++) { 9169 cue = cues[i]; 9170 9171 // Compute the intial position and styles of the cue div. 9172 styleBox = new CueStyleBox(window, cue, styleOptions); 9173 paddedOverlay.appendChild(styleBox.div); 9174 9175 // Move the cue div to it's correct line position. 9176 moveBoxToLinePosition(window, styleBox, containerBox, boxPositions); 9177 9178 // Remember the computed div so that we don't have to recompute it later 9179 // if we don't have too. 9180 cue.displayState = styleBox.div; 9181 9182 boxPositions.push(BoxPosition.getSimpleBoxPosition(styleBox)); 9183 } 9184 })(); 9185}; 9186 9187WebVTT$1.Parser = function(window, vttjs, decoder) { 9188 if (!decoder) { 9189 decoder = vttjs; 9190 vttjs = {}; 9191 } 9192 if (!vttjs) { 9193 vttjs = {}; 9194 } 9195 9196 this.window = window; 9197 this.vttjs = vttjs; 9198 this.state = "INITIAL"; 9199 this.buffer = ""; 9200 this.decoder = decoder || new TextDecoder("utf8"); 9201 this.regionList = []; 9202}; 9203 9204WebVTT$1.Parser.prototype = { 9205 // If the error is a ParsingError then report it to the consumer if 9206 // possible. If it's not a ParsingError then throw it like normal. 9207 reportOrThrowError: function(e) { 9208 if (e instanceof ParsingError) { 9209 this.onparsingerror && this.onparsingerror(e); 9210 } else { 9211 throw e; 9212 } 9213 }, 9214 parse: function (data) { 9215 var self = this; 9216 9217 // If there is no data then we won't decode it, but will just try to parse 9218 // whatever is in buffer already. This may occur in circumstances, for 9219 // example when flush() is called. 9220 if (data) { 9221 // Try to decode the data that we received. 9222 self.buffer += self.decoder.decode(data, {stream: true}); 9223 } 9224 9225 function collectNextLine() { 9226 var buffer = self.buffer; 9227 var pos = 0; 9228 while (pos < buffer.length && buffer[pos] !== '\r' && buffer[pos] !== '\n') { 9229 ++pos; 9230 } 9231 var line = buffer.substr(0, pos); 9232 // Advance the buffer early in case we fail below. 9233 if (buffer[pos] === '\r') { 9234 ++pos; 9235 } 9236 if (buffer[pos] === '\n') { 9237 ++pos; 9238 } 9239 self.buffer = buffer.substr(pos); 9240 return line; 9241 } 9242 9243 // 3.4 WebVTT region and WebVTT region settings syntax 9244 function parseRegion(input) { 9245 var settings = new Settings(); 9246 9247 parseOptions(input, function (k, v) { 9248 switch (k) { 9249 case "id": 9250 settings.set(k, v); 9251 break; 9252 case "width": 9253 settings.percent(k, v); 9254 break; 9255 case "lines": 9256 settings.integer(k, v); 9257 break; 9258 case "regionanchor": 9259 case "viewportanchor": 9260 var xy = v.split(','); 9261 if (xy.length !== 2) { 9262 break; 9263 } 9264 // We have to make sure both x and y parse, so use a temporary 9265 // settings object here. 9266 var anchor = new Settings(); 9267 anchor.percent("x", xy[0]); 9268 anchor.percent("y", xy[1]); 9269 if (!anchor.has("x") || !anchor.has("y")) { 9270 break; 9271 } 9272 settings.set(k + "X", anchor.get("x")); 9273 settings.set(k + "Y", anchor.get("y")); 9274 break; 9275 case "scroll": 9276 settings.alt(k, v, ["up"]); 9277 break; 9278 } 9279 }, /=/, /\s/); 9280 9281 // Create the region, using default values for any values that were not 9282 // specified. 9283 if (settings.has("id")) { 9284 var region = new (self.vttjs.VTTRegion || self.window.VTTRegion)(); 9285 region.width = settings.get("width", 100); 9286 region.lines = settings.get("lines", 3); 9287 region.regionAnchorX = settings.get("regionanchorX", 0); 9288 region.regionAnchorY = settings.get("regionanchorY", 100); 9289 region.viewportAnchorX = settings.get("viewportanchorX", 0); 9290 region.viewportAnchorY = settings.get("viewportanchorY", 100); 9291 region.scroll = settings.get("scroll", ""); 9292 // Register the region. 9293 self.onregion && self.onregion(region); 9294 // Remember the VTTRegion for later in case we parse any VTTCues that 9295 // reference it. 9296 self.regionList.push({ 9297 id: settings.get("id"), 9298 region: region 9299 }); 9300 } 9301 } 9302 9303 // draft-pantos-http-live-streaming-20 9304 // https://tools.ietf.org/html/draft-pantos-http-live-streaming-20#section-3.5 9305 // 3.5 WebVTT 9306 function parseTimestampMap(input) { 9307 var settings = new Settings(); 9308 9309 parseOptions(input, function(k, v) { 9310 switch(k) { 9311 case "MPEGT": 9312 settings.integer(k + 'S', v); 9313 break; 9314 case "LOCA": 9315 settings.set(k + 'L', parseTimeStamp(v)); 9316 break; 9317 } 9318 }, /[^\d]:/, /,/); 9319 9320 self.ontimestampmap && self.ontimestampmap({ 9321 "MPEGTS": settings.get("MPEGTS"), 9322 "LOCAL": settings.get("LOCAL") 9323 }); 9324 } 9325 9326 // 3.2 WebVTT metadata header syntax 9327 function parseHeader(input) { 9328 if (input.match(/X-TIMESTAMP-MAP/)) { 9329 // This line contains HLS X-TIMESTAMP-MAP metadata 9330 parseOptions(input, function(k, v) { 9331 switch(k) { 9332 case "X-TIMESTAMP-MAP": 9333 parseTimestampMap(v); 9334 break; 9335 } 9336 }, /=/); 9337 } else { 9338 parseOptions(input, function (k, v) { 9339 switch (k) { 9340 case "Region": 9341 // 3.3 WebVTT region metadata header syntax 9342 parseRegion(v); 9343 break; 9344 } 9345 }, /:/); 9346 } 9347 9348 } 9349 9350 // 5.1 WebVTT file parsing. 9351 try { 9352 var line; 9353 if (self.state === "INITIAL") { 9354 // We can't start parsing until we have the first line. 9355 if (!/\r\n|\n/.test(self.buffer)) { 9356 return this; 9357 } 9358 9359 line = collectNextLine(); 9360 9361 var m = line.match(/^WEBVTT([ \t].*)?$/); 9362 if (!m || !m[0]) { 9363 throw new ParsingError(ParsingError.Errors.BadSignature); 9364 } 9365 9366 self.state = "HEADER"; 9367 } 9368 9369 var alreadyCollectedLine = false; 9370 while (self.buffer) { 9371 // We can't parse a line until we have the full line. 9372 if (!/\r\n|\n/.test(self.buffer)) { 9373 return this; 9374 } 9375 9376 if (!alreadyCollectedLine) { 9377 line = collectNextLine(); 9378 } else { 9379 alreadyCollectedLine = false; 9380 } 9381 9382 switch (self.state) { 9383 case "HEADER": 9384 // 13-18 - Allow a header (metadata) under the WEBVTT line. 9385 if (/:/.test(line)) { 9386 parseHeader(line); 9387 } else if (!line) { 9388 // An empty line terminates the header and starts the body (cues). 9389 self.state = "ID"; 9390 } 9391 continue; 9392 case "NOTE": 9393 // Ignore NOTE blocks. 9394 if (!line) { 9395 self.state = "ID"; 9396 } 9397 continue; 9398 case "ID": 9399 // Check for the start of NOTE blocks. 9400 if (/^NOTE($|[ \t])/.test(line)) { 9401 self.state = "NOTE"; 9402 break; 9403 } 9404 // 19-29 - Allow any number of line terminators, then initialize new cue values. 9405 if (!line) { 9406 continue; 9407 } 9408 self.cue = new (self.vttjs.VTTCue || self.window.VTTCue)(0, 0, ""); 9409
vendor: 2,935 bytes, lines 9409-9499
9409self.state = "CUE"; 9410 // 30-39 - Check if self line contains an optional identifier or timing data. 9411 if (line.indexOf("-->") === -1) { 9412 self.cue.id = line; 9413 continue; 9414 } 9415 // Process line as start of a cue. 9416 /*falls through*/ 9417 case "CUE": 9418 // 40 - Collect cue timings and settings. 9419 try { 9420 parseCue(line, self.cue, self.regionList); 9421 } catch (e) { 9422 self.reportOrThrowError(e); 9423 // In case of an error ignore rest of the cue. 9424 self.cue = null; 9425 self.state = "BADCUE"; 9426 continue; 9427 } 9428 self.state = "CUETEXT"; 9429 continue; 9430 case "CUETEXT": 9431 var hasSubstring = line.indexOf("-->") !== -1; 9432 // 34 - If we have an empty line then report the cue. 9433 // 35 - If we have the special substring '-->' then report the cue, 9434 // but do not collect the line as we need to process the current 9435 // one as a new cue. 9436 if (!line || hasSubstring && (alreadyCollectedLine = true)) { 9437 // We are done parsing self cue. 9438 self.oncue && self.oncue(self.cue); 9439 self.cue = null; 9440 self.state = "ID"; 9441 continue; 9442 } 9443 if (self.cue.text) { 9444 self.cue.text += "\n"; 9445 } 9446 self.cue.text += line; 9447 continue; 9448 case "BADCUE": // BADCUE 9449 // 54-62 - Collect and discard the remaining cue. 9450 if (!line) { 9451 self.state = "ID"; 9452 } 9453 continue; 9454 } 9455 } 9456 } catch (e) { 9457 self.reportOrThrowError(e); 9458 9459 // If we are currently parsing a cue, report what we have. 9460 if (self.state === "CUETEXT" && self.cue && self.oncue) { 9461 self.oncue(self.cue); 9462 } 9463 self.cue = null; 9464 // Enter BADWEBVTT state if header was not parsed correctly otherwise 9465 // another exception occurred so enter BADCUE state. 9466 self.state = self.state === "INITIAL" ? "BADWEBVTT" : "BADCUE"; 9467 } 9468 return this; 9469 }, 9470 flush: function () { 9471 var self = this; 9472 try { 9473 // Finish decoding the stream. 9474 self.buffer += self.decoder.decode(); 9475 // Synthesize the end of the current cue or region. 9476 if (self.cue || self.state === "HEADER") { 9477 self.buffer += "\n\n"; 9478 self.parse(); 9479 } 9480 // If we've flushed, parsed, and we're still on the INITIAL state then 9481 // that means we don't have enough of the stream to parse the first 9482 // line. 9483 if (self.state === "INITIAL") { 9484 throw new ParsingError(ParsingError.Errors.BadSignature); 9485 } 9486 } catch(e) { 9487 self.reportOrThrowError(e); 9488 } 9489 self.onflush && self.onflush(); 9490 return this; 9491 } 9492}; 9493 9494var vtt$1 = WebVTT$1; 9495 9496/** 9497 * Copyright 2013 vtt.js Contributors 9498 * 9499 * Licensed under the Apache License, Version 2.0 (the "License");
9500 * you may not use this file except in compliance with the License. 9501 * You may obtain a copy of the License at 9502 * 9503 * http://www.apache.org/licenses/LICENSE-2.0 9504 * 9505 * Unless required by applicable law or agreed to in writing, software 9506 * distributed under the License is distributed on an "AS IS" BASIS, 9507 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9508 * See the License for the specific language governing permissions and 9509 * limitations under the License. 9510 */ 9511 9512var autoKeyword = "auto"; 9513var directionSetting = { 9514 "": true, 9515 "lr": true, 9516 "rl": true 9517}; 9518var alignSetting = { 9519 "start": true, 9520 "middle": true, 9521 "end": true, 9522 "left": true, 9523 "right": true 9524}; 9525 9526function findDirectionSetting(value) { 9527 if (typeof value !== "string") { 9528 return false; 9529 } 9530 var dir = directionSetting[value.toLowerCase()]; 9531 return dir ? value.toLowerCase() : false; 9532} 9533 9534function findAlignSetting(value) { 9535 if (typeof value !== "string") { 9536 return false; 9537 } 9538 var align = alignSetting[value.toLowerCase()]; 9539 return align ? value.toLowerCase() : false; 9540} 9541 9542function extend$1(obj) { 9543 var i = 1; 9544 for (; i < arguments.length; i++) { 9545 var cobj = arguments[i]; 9546 for (var p in cobj) { 9547 obj[p] = cobj[p]; 9548 } 9549 } 9550 9551 return obj; 9552} 9553 9554function VTTCue(startTime, endTime, text) { 9555 var cue = this; 9556 var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent); 9557 var baseObj = {}; 9558 9559 if (isIE8) { 9560 cue = document.createElement('custom'); 9561 } else { 9562 baseObj.enumerable = true; 9563 } 9564 9565 /** 9566 * Shim implementation specific properties. These properties are not in 9567 * the spec. 9568 */ 9569 9570 // Lets us know when the VTTCue's data has changed in such a way that we need 9571 // to recompute its display state. This lets us compute its display state 9572 // lazily. 9573 cue.hasBeenReset = false; 9574 9575 /** 9576 * VTTCue and TextTrackCue properties 9577 * http://dev.w3.org/html5/webvtt/#vttcue-interface 9578 */ 9579 9580 var _id = ""; 9581 var _pauseOnExit = false; 9582 var _startTime = startTime; 9583 var _endTime = endTime; 9584 var _text = text; 9585 var _region = null; 9586 var _vertical = ""; 9587 var _snapToLines = true; 9588 var _line = "auto"; 9589 var _lineAlign = "start"; 9590 var _position = 50; 9591 var _positionAlign = "middle"; 9592 var _size = 50; 9593 var _align = "middle"; 9594 9595 Object.defineProperty(cue, 9596 "id", extend$1({}, baseObj, { 9597 get: function() { 9598 return _id; 9599 }, 9600 set: function(value) { 9601 _id = "" + value; 9602 } 9603 })); 9604 9605 Object.defineProperty(cue, 9606 "pauseOnExit", extend$1({}, baseObj, { 9607 get: function() { 9608 return _pauseOnExit; 9609 }, 9610 set: function(value) { 9611 _pauseOnExit = !!value; 9612 } 9613 })); 9614 9615 Object.defineProperty(cue, 9616 "startTime", extend$1({}, baseObj, { 9617 get: function() { 9618 return _startTime; 9619 }, 9620 set: function(value) { 9621 if (typeof value !== "number") { 9622 throw new TypeError("Start time must be set to a number."); 9623 } 9624 _startTime = value; 9625 this.hasBeenReset = true; 9626 } 9627 })); 9628 9629 Object.defineProperty(cue, 9630 "endTime", extend$1({}, baseObj, { 9631 get: function() { 9632 return _endTime; 9633 }, 9634 set: function(value) { 9635 if (typeof value !== "number") { 9636 throw new TypeError("End time must be set to a number."); 9637 } 9638 _endTime = value; 9639 this.hasBeenReset = true; 9640 } 9641 })); 9642 9643 Object.defineProperty(cue, 9644 "text", extend$1({}, baseObj, { 9645 get: function() { 9646 return _text; 9647 }, 9648 set: function(value) { 9649 _text = "" + value; 9650 this.hasBeenReset = true; 9651 } 9652 })); 9653 9654 Object.defineProperty(cue, 9655 "region", extend$1({}, baseObj, { 9656 get: function() { 9657 return _region; 9658 }, 9659 set: function(value) { 9660 _region = value; 9661 this.hasBeenReset = true; 9662 } 9663 })); 9664 9665 Object.defineProperty(cue, 9666 "vertical", extend$1({}, baseObj, { 9667 get: function() { 9668 return _vertical; 9669 }, 9670 set: function(value) { 9671 var setting = findDirectionSetting(value); 9672 // Have to check for false because the setting an be an empty string. 9673 if (setting === false) { 9674 throw new SyntaxError("An invalid or illegal string was specified."); 9675 } 9676 _vertical = setting; 9677 this.hasBeenReset = true; 9678 } 9679 })); 9680 9681 Object.defineProperty(cue, 9682 "snapToLines", extend$1({}, baseObj, { 9683 get: function() { 9684 return _snapToLines; 9685 }, 9686 set: function(value) { 9687 _snapToLines = !!value; 9688 this.hasBeenReset = true; 9689 } 9690 })); 9691 9692 Object.defineProperty(cue, 9693 "line", extend$1({}, baseObj, { 9694 get: function() { 9695 return _line; 9696 }, 9697 set: function(value) {
vendor: 4,245 bytes, lines 9698-9863
9698 if (typeof value !== "number" && value !== autoKeyword) { 9699 throw new SyntaxError("An invalid number or illegal string was specified."); 9700 } 9701 _line = value; 9702 this.hasBeenReset = true; 9703 } 9704 })); 9705 9706 Object.defineProperty(cue, 9707 "lineAlign", extend$1({}, baseObj, { 9708 get: function() { 9709 return _lineAlign; 9710 }, 9711 set: function(value) { 9712 var setting = findAlignSetting(value); 9713 if (!setting) { 9714 throw new SyntaxError("An invalid or illegal string was specified."); 9715 } 9716 _lineAlign = setting; 9717 this.hasBeenReset = true; 9718 } 9719 })); 9720 9721 Object.defineProperty(cue, 9722 "position", extend$1({}, baseObj, { 9723 get: function() { 9724 return _position; 9725 }, 9726 set: function(value) { 9727 if (value < 0 || value > 100) { 9728 throw new Error("Position must be between 0 and 100."); 9729 } 9730 _position = value; 9731 this.hasBeenReset = true; 9732 } 9733 })); 9734 9735 Object.defineProperty(cue, 9736 "positionAlign", extend$1({}, baseObj, { 9737 get: function() { 9738 return _positionAlign; 9739 }, 9740 set: function(value) { 9741 var setting = findAlignSetting(value); 9742 if (!setting) { 9743 throw new SyntaxError("An invalid or illegal string was specified."); 9744 } 9745 _positionAlign = setting; 9746 this.hasBeenReset = true; 9747 } 9748 })); 9749 9750 Object.defineProperty(cue, 9751 "size", extend$1({}, baseObj, { 9752 get: function() { 9753 return _size; 9754 }, 9755 set: function(value) { 9756 if (value < 0 || value > 100) { 9757 throw new Error("Size must be between 0 and 100."); 9758 } 9759 _size = value; 9760 this.hasBeenReset = true; 9761 } 9762 })); 9763 9764 Object.defineProperty(cue, 9765 "align", extend$1({}, baseObj, { 9766 get: function() { 9767 return _align; 9768 }, 9769 set: function(value) { 9770 var setting = findAlignSetting(value); 9771 if (!setting) { 9772 throw new SyntaxError("An invalid or illegal string was specified."); 9773 } 9774 _align = setting; 9775 this.hasBeenReset = true; 9776 } 9777 })); 9778 9779 /** 9780 * Other <track> spec defined properties 9781 */ 9782 9783 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#text-track-cue-display-state 9784 cue.displayState = undefined; 9785 9786 if (isIE8) { 9787 return cue; 9788 } 9789} 9790 9791/** 9792 * VTTCue methods 9793 */ 9794 9795VTTCue.prototype.getCueAsHTML = function() { 9796 // Assume WebVTT.convertCueToDOMTree is on the global. 9797 return WebVTT.convertCueToDOMTree(window, this.text); 9798}; 9799 9800var vttcue = VTTCue; 9801 9802/** 9803 * Copyright 2013 vtt.js Contributors 9804 * 9805 * Licensed under the Apache License, Version 2.0 (the "License"); 9806 * you may not use this file except in compliance with the License. 9807 * You may obtain a copy of the License at 9808 * 9809 * http://www.apache.org/licenses/LICENSE-2.0 9810 * 9811 * Unless required by applicable law or agreed to in writing, software 9812 * distributed under the License is distributed on an "AS IS" BASIS, 9813 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9814 * See the License for the specific language governing permissions and 9815 * limitations under the License. 9816 */ 9817 9818var scrollSetting = { 9819 "": true, 9820 "up": true 9821}; 9822 9823function findScrollSetting(value) { 9824 if (typeof value !== "string") { 9825 return false; 9826 } 9827 var scroll = scrollSetting[value.toLowerCase()]; 9828 return scroll ? value.toLowerCase() : false; 9829} 9830 9831function isValidPercentValue(value) { 9832 return typeof value === "number" && (value >= 0 && value <= 100); 9833} 9834 9835// VTTRegion shim http://dev.w3.org/html5/webvtt/#vttregion-interface 9836function VTTRegion() { 9837 var _width = 100; 9838 var _lines = 3; 9839 var _regionAnchorX = 0; 9840 var _regionAnchorY = 100; 9841 var _viewportAnchorX = 0; 9842 var _viewportAnchorY = 100; 9843 var _scroll = ""; 9844 9845 Object.defineProperties(this, { 9846 "width": { 9847 enumerable: true, 9848 get: function() { 9849 return _width; 9850 }, 9851 set: function(value) { 9852 if (!isValidPercentValue(value)) { 9853 throw new Error("Width must be between 0 and 100."); 9854 } 9855 _width = value; 9856 } 9857 }, 9858 "lines": { 9859 enumerable: true, 9860 get: function() { 9861 return _lines; 9862 }, 9863 set: function(value) {
vendor: 281,102 bytes, lines 9864-20209
9864 if (typeof value !== "number") { 9865 throw new TypeError("Lines must be set to a number."); 9866 } 9867 _lines = value; 9868 } 9869 }, 9870 "regionAnchorY": { 9871 enumerable: true, 9872 get: function() { 9873 return _regionAnchorY; 9874 }, 9875 set: function(value) { 9876 if (!isValidPercentValue(value)) { 9877 throw new Error("RegionAnchorX must be between 0 and 100."); 9878 } 9879 _regionAnchorY = value; 9880 } 9881 }, 9882 "regionAnchorX": { 9883 enumerable: true, 9884 get: function() { 9885 return _regionAnchorX; 9886 }, 9887 set: function(value) { 9888 if(!isValidPercentValue(value)) { 9889 throw new Error("RegionAnchorY must be between 0 and 100."); 9890 } 9891 _regionAnchorX = value; 9892 } 9893 }, 9894 "viewportAnchorY": { 9895 enumerable: true, 9896 get: function() { 9897 return _viewportAnchorY; 9898 }, 9899 set: function(value) { 9900 if (!isValidPercentValue(value)) { 9901 throw new Error("ViewportAnchorY must be between 0 and 100."); 9902 } 9903 _viewportAnchorY = value; 9904 } 9905 }, 9906 "viewportAnchorX": { 9907 enumerable: true, 9908 get: function() { 9909 return _viewportAnchorX; 9910 }, 9911 set: function(value) { 9912 if (!isValidPercentValue(value)) { 9913 throw new Error("ViewportAnchorX must be between 0 and 100."); 9914 } 9915 _viewportAnchorX = value; 9916 } 9917 }, 9918 "scroll": { 9919 enumerable: true, 9920 get: function() { 9921 return _scroll; 9922 }, 9923 set: function(value) { 9924 var setting = findScrollSetting(value); 9925 // Have to check for false as an empty string is a legal value. 9926 if (setting === false) { 9927 throw new SyntaxError("An invalid or illegal string was specified."); 9928 } 9929 _scroll = setting; 9930 } 9931 } 9932 }); 9933} 9934 9935var vttregion = VTTRegion; 9936 9937var browserIndex = createCommonjsModule(function (module) { 9938/** 9939 * Copyright 2013 vtt.js Contributors 9940 * 9941 * Licensed under the Apache License, Version 2.0 (the "License"); 9942 * you may not use this file except in compliance with the License. 9943 * You may obtain a copy of the License at 9944 * 9945 * http://www.apache.org/licenses/LICENSE-2.0 9946 * 9947 * Unless required by applicable law or agreed to in writing, software 9948 * distributed under the License is distributed on an "AS IS" BASIS, 9949 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9950 * See the License for the specific language governing permissions and 9951 * limitations under the License. 9952 */ 9953 9954// Default exports for Node. Export the extended versions of VTTCue and 9955// VTTRegion in Node since we likely want the capability to convert back and 9956// forth between JSON. If we don't then it's not that big of a deal since we're 9957// off browser. 9958 9959 9960 9961var vttjs = module.exports = { 9962 WebVTT: vtt$1, 9963 VTTCue: vttcue, 9964 VTTRegion: vttregion 9965}; 9966 9967window_1.vttjs = vttjs; 9968window_1.WebVTT = vttjs.WebVTT; 9969 9970var cueShim = vttjs.VTTCue; 9971var regionShim = vttjs.VTTRegion; 9972var nativeVTTCue = window_1.VTTCue; 9973var nativeVTTRegion = window_1.VTTRegion; 9974 9975vttjs.shim = function() { 9976 window_1.VTTCue = cueShim; 9977 window_1.VTTRegion = regionShim; 9978}; 9979 9980vttjs.restore = function() { 9981 window_1.VTTCue = nativeVTTCue; 9982 window_1.VTTRegion = nativeVTTRegion; 9983}; 9984 9985if (!window_1.VTTCue) { 9986 vttjs.shim(); 9987} 9988}); 9989 9990/** 9991 * @file tech.js 9992 */ 9993 9994/** 9995 * An Object containing a structure like: `{src: 'url', type: 'mimetype'}` or string 9996 * that just contains the src url alone. 9997 * * `var SourceObject = {src: 'http://ex.com/video.mp4', type: 'video/mp4'};` 9998 * `var SourceString = 'http://example.com/some-video.mp4';` 9999 * 10000 * @typedef {Object|string} Tech~SourceObject 10001 * 10002 * @property {string} src 10003 * The url to the source 10004 * 10005 * @property {string} type 10006 * The mime type of the source 10007 */ 10008 10009/** 10010 * A function used by {@link Tech} to create a new {@link TextTrack}. 10011 * 10012 * @private 10013 * 10014 * @param {Tech} self 10015 * An instance of the Tech class. 10016 * 10017 * @param {string} kind 10018 * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata) 10019 * 10020 * @param {string} [label] 10021 * Label to identify the text track 10022 * 10023 * @param {string} [language] 10024 * Two letter language abbreviation 10025 * 10026 * @param {Object} [options={}] 10027 * An object with additional text track options 10028 * 10029 * @return {TextTrack} 10030 * The text track that was created. 10031 */ 10032function createTrackHelper(self, kind, label, language) { 10033 var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; 10034 10035 var tracks = self.textTracks(); 10036 10037 options.kind = kind; 10038 10039 if (label) { 10040 options.label = label; 10041 } 10042 if (language) { 10043 options.language = language; 10044 } 10045 options.tech = self; 10046 10047 var track = new ALL.text.TrackClass(options); 10048 10049 tracks.addTrack(track); 10050 10051 return track; 10052} 10053 10054/** 10055 * This is the base class for media playback technology controllers, such as 10056 * {@link Flash} and {@link HTML5} 10057 * 10058 * @extends Component 10059 */ 10060 10061var Tech = function (_Component) { 10062 inherits(Tech, _Component); 10063 10064 /** 10065 * Create an instance of this Tech. 10066 * 10067 * @param {Object} [options] 10068 * The key/value store of player options. 10069 * 10070 * @param {Component~ReadyCallback} ready 10071 * Callback function to call when the `HTML5` Tech is ready. 10072 */ 10073 function Tech() { 10074 var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 10075 var ready = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {}; 10076 classCallCheck(this, Tech); 10077 10078 // we don't want the tech to report user activity automatically. 10079 // This is done manually in addControlsListeners 10080 options.reportTouchActivity = false; 10081 10082 // keep track of whether the current source has played at all to 10083 // implement a very limited played() 10084 var _this = possibleConstructorReturn(this, _Component.call(this, null, options, ready)); 10085 10086 _this.hasStarted_ = false; 10087 _this.on('playing', function () { 10088 this.hasStarted_ = true; 10089 }); 10090 _this.on('loadstart', function () { 10091 this.hasStarted_ = false; 10092 }); 10093 10094 ALL.names.forEach(function (name) { 10095 var props = ALL[name]; 10096 10097 if (options && options[props.getterName]) { 10098 _this[props.privateName] = options[props.getterName]; 10099 } 10100 }); 10101 10102 // Manually track progress in cases where the browser/flash player doesn't report it. 10103 if (!_this.featuresProgressEvents) { 10104 _this.manualProgressOn(); 10105 } 10106 10107 // Manually track timeupdates in cases where the browser/flash player doesn't report it. 10108 if (!_this.featuresTimeupdateEvents) { 10109 _this.manualTimeUpdatesOn(); 10110 } 10111 10112 ['Text', 'Audio', 'Video'].forEach(function (track) { 10113 if (options['native' + track + 'Tracks'] === false) { 10114 _this['featuresNative' + track + 'Tracks'] = false; 10115 } 10116 }); 10117 10118 if (options.nativeCaptions === false || options.nativeTextTracks === false) { 10119 _this.featuresNativeTextTracks = false; 10120 } else if (options.nativeCaptions === true || options.nativeTextTracks === true) { 10121 _this.featuresNativeTextTracks = true; 10122 } 10123 10124 if (!_this.featuresNativeTextTracks) { 10125 _this.emulateTextTracks(); 10126 } 10127 10128 _this.autoRemoteTextTracks_ = new ALL.text.ListClass(); 10129 10130 _this.initTrackListeners(); 10131 10132 // Turn on component tap events only if not using native controls 10133 if (!options.nativeControlsForTouch) { 10134 _this.emitTapEvents(); 10135 } 10136 10137 if (_this.constructor) { 10138 _this.name_ = _this.constructor.name || 'Unknown Tech'; 10139 } 10140 return _this; 10141 } 10142 10143 /* Fallbacks for unsupported event types 10144 ================================================================================ */ 10145 10146 /** 10147 * Polyfill the `progress` event for browsers that don't support it natively. 10148 * 10149 * @see {@link Tech#trackProgress} 10150 */ 10151 10152 10153 Tech.prototype.manualProgressOn = function manualProgressOn() { 10154 this.on('durationchange', this.onDurationChange); 10155 10156 this.manualProgress = true; 10157 10158 // Trigger progress watching when a source begins loading 10159 this.one('ready', this.trackProgress); 10160 }; 10161 10162 /** 10163 * Turn off the polyfill for `progress` events that was created in 10164 * {@link Tech#manualProgressOn} 10165 */ 10166 10167 10168 Tech.prototype.manualProgressOff = function manualProgressOff() { 10169 this.manualProgress = false; 10170 this.stopTrackingProgress(); 10171 10172 this.off('durationchange', this.onDurationChange); 10173 }; 10174 10175 /** 10176 * This is used to trigger a `progress` event when the buffered percent changes. It 10177 * sets an interval function that will be called every 500 milliseconds to check if the 10178 * buffer end percent has changed. 10179 * 10180 * > This function is called by {@link Tech#manualProgressOn} 10181 * 10182 * @param {EventTarget~Event} event 10183 * The `ready` event that caused this to run. 10184 * 10185 * @listens Tech#ready 10186 * @fires Tech#progress 10187 */ 10188 10189 10190 Tech.prototype.trackProgress = function trackProgress(event) { 10191 this.stopTrackingProgress(); 10192 this.progressInterval = this.setInterval(bind(this, function () { 10193 // Don't trigger unless buffered amount is greater than last time 10194 10195 var numBufferedPercent = this.bufferedPercent(); 10196 10197 if (this.bufferedPercent_ !== numBufferedPercent) { 10198 /** 10199 * See {@link Player#progress} 10200 * 10201 * @event Tech#progress 10202 * @type {EventTarget~Event} 10203 */ 10204 this.trigger('progress'); 10205 } 10206 10207 this.bufferedPercent_ = numBufferedPercent; 10208 10209 if (numBufferedPercent === 1) { 10210 this.stopTrackingProgress(); 10211 } 10212 }), 500); 10213 }; 10214 10215 /** 10216 * Update our internal duration on a `durationchange` event by calling 10217 * {@link Tech#duration}. 10218 * 10219 * @param {EventTarget~Event} event 10220 * The `durationchange` event that caused this to run. 10221 * 10222 * @listens Tech#durationchange 10223 */ 10224 10225 10226 Tech.prototype.onDurationChange = function onDurationChange(event) { 10227 this.duration_ = this.duration(); 10228 }; 10229 10230 /** 10231 * Get and create a `TimeRange` object for buffering. 10232 * 10233 * @return {TimeRange} 10234 * The time range object that was created. 10235 */ 10236 10237 10238 Tech.prototype.buffered = function buffered() { 10239 return createTimeRanges(0, 0); 10240 }; 10241 10242 /** 10243 * Get the percentage of the current video that is currently buffered. 10244 * 10245 * @return {number} 10246 * A number from 0 to 1 that represents the decimal percentage of the 10247 * video that is buffered. 10248 * 10249 */ 10250 10251 10252 Tech.prototype.bufferedPercent = function bufferedPercent$$1() { 10253 return bufferedPercent(this.buffered(), this.duration_); 10254 }; 10255 10256 /** 10257 * Turn off the polyfill for `progress` events that was created in 10258 * {@link Tech#manualProgressOn} 10259 * Stop manually tracking progress events by clearing the interval that was set in 10260 * {@link Tech#trackProgress}. 10261 */ 10262 10263 10264 Tech.prototype.stopTrackingProgress = function stopTrackingProgress() { 10265 this.clearInterval(this.progressInterval); 10266 }; 10267 10268 /** 10269 * Polyfill the `timeupdate` event for browsers that don't support it. 10270 * 10271 * @see {@link Tech#trackCurrentTime} 10272 */ 10273 10274 10275 Tech.prototype.manualTimeUpdatesOn = function manualTimeUpdatesOn() { 10276 this.manualTimeUpdates = true; 10277 10278 this.on('play', this.trackCurrentTime); 10279 this.on('pause', this.stopTrackingCurrentTime); 10280 }; 10281 10282 /** 10283 * Turn off the polyfill for `timeupdate` events that was created in 10284 * {@link Tech#manualTimeUpdatesOn} 10285 */ 10286 10287 10288 Tech.prototype.manualTimeUpdatesOff = function manualTimeUpdatesOff() { 10289 this.manualTimeUpdates = false; 10290 this.stopTrackingCurrentTime(); 10291 this.off('play', this.trackCurrentTime); 10292 this.off('pause', this.stopTrackingCurrentTime); 10293 }; 10294 10295 /** 10296 * Sets up an interval function to track current time and trigger `timeupdate` every 10297 * 250 milliseconds. 10298 * 10299 * @listens Tech#play 10300 * @triggers Tech#timeupdate 10301 */ 10302 10303 10304 Tech.prototype.trackCurrentTime = function trackCurrentTime() { 10305 if (this.currentTimeInterval) { 10306 this.stopTrackingCurrentTime(); 10307 } 10308 this.currentTimeInterval = this.setInterval(function () { 10309 /** 10310 * Triggered at an interval of 250ms to indicated that time is passing in the video. 10311 * 10312 * @event Tech#timeupdate 10313 * @type {EventTarget~Event} 10314 */ 10315 this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true }); 10316 10317 // 42 = 24 fps // 250 is what Webkit uses // FF uses 15 10318 }, 250); 10319 }; 10320 10321 /** 10322 * Stop the interval function created in {@link Tech#trackCurrentTime} so that the 10323 * `timeupdate` event is no longer triggered. 10324 * 10325 * @listens {Tech#pause} 10326 */ 10327 10328 10329 Tech.prototype.stopTrackingCurrentTime = function stopTrackingCurrentTime() { 10330 this.clearInterval(this.currentTimeInterval); 10331 10332 // #1002 - if the video ends right before the next timeupdate would happen, 10333 // the progress bar won't make it all the way to the end 10334 this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true }); 10335 }; 10336 10337 /** 10338 * Turn off all event polyfills, clear the `Tech`s {@link AudioTrackList}, 10339 * {@link VideoTrackList}, and {@link TextTrackList}, and dispose of this Tech. 10340 * 10341 * @fires Component#dispose 10342 */ 10343 10344 10345 Tech.prototype.dispose = function dispose() { 10346 10347 // clear out all tracks because we can't reuse them between techs 10348 this.clearTracks(NORMAL.names); 10349 10350 // Turn off any manual progress or timeupdate tracking 10351 if (this.manualProgress) { 10352 this.manualProgressOff(); 10353 } 10354 10355 if (this.manualTimeUpdates) { 10356 this.manualTimeUpdatesOff(); 10357 } 10358 10359 _Component.prototype.dispose.call(this); 10360 }; 10361 10362 /** 10363 * Clear out a single `TrackList` or an array of `TrackLists` given their names. 10364 * 10365 * > Note: Techs without source handlers should call this between sources for `video` 10366 * & `audio` tracks. You don't want to use them between tracks! 10367 * 10368 * @param {string[]|string} types 10369 * TrackList names to clear, valid names are `video`, `audio`, and 10370 * `text`. 10371 */ 10372 10373 10374 Tech.prototype.clearTracks = function clearTracks(types) { 10375 var _this2 = this; 10376 10377 types = [].concat(types); 10378 // clear out all tracks because we can't reuse them between techs 10379 types.forEach(function (type) { 10380 var list = _this2[type + 'Tracks']() || []; 10381 var i = list.length; 10382 10383 while (i--) { 10384 var track = list[i]; 10385 10386 if (type === 'text') { 10387 _this2.removeRemoteTextTrack(track); 10388 } 10389 list.removeTrack(track); 10390 } 10391 }); 10392 }; 10393 10394 /** 10395 * Remove any TextTracks added via addRemoteTextTrack that are 10396 * flagged for automatic garbage collection 10397 */ 10398 10399 10400 Tech.prototype.cleanupAutoTextTracks = function cleanupAutoTextTracks() { 10401 var list = this.autoRemoteTextTracks_ || []; 10402 var i = list.length; 10403 10404 while (i--) { 10405 var track = list[i]; 10406 10407 this.removeRemoteTextTrack(track); 10408 } 10409 }; 10410 10411 /** 10412 * Reset the tech, which will removes all sources and reset the internal readyState. 10413 * 10414 * @abstract 10415 */ 10416 10417 10418 Tech.prototype.reset = function reset() {}; 10419 10420 /** 10421 * Get or set an error on the Tech. 10422 * 10423 * @param {MediaError} [err] 10424 * Error to set on the Tech 10425 * 10426 * @return {MediaError|null} 10427 * The current error object on the tech, or null if there isn't one. 10428 */ 10429 10430 10431 Tech.prototype.error = function error(err) { 10432 if (err !== undefined) { 10433 this.error_ = new MediaError(err); 10434 this.trigger('error'); 10435 } 10436 return this.error_; 10437 }; 10438 10439 /** 10440 * Returns the `TimeRange`s that have been played through for the current source. 10441 * 10442 * > NOTE: This implementation is incomplete. It does not track the played `TimeRange`. 10443 * It only checks wether the source has played at all or not. 10444 * 10445 * @return {TimeRange} 10446 * - A single time range if this video has played 10447 * - An empty set of ranges if not. 10448 */ 10449 10450 10451 Tech.prototype.played = function played() { 10452 if (this.hasStarted_) { 10453 return createTimeRanges(0, 0); 10454 } 10455 return createTimeRanges(); 10456 }; 10457 10458 /** 10459 * Causes a manual time update to occur if {@link Tech#manualTimeUpdatesOn} was 10460 * previously called. 10461 * 10462 * @fires Tech#timeupdate 10463 */ 10464 10465 10466 Tech.prototype.setCurrentTime = function setCurrentTime() { 10467 // improve the accuracy of manual timeupdates 10468 if (this.manualTimeUpdates) { 10469 /** 10470 * A manual `timeupdate` event. 10471 * 10472 * @event Tech#timeupdate 10473 * @type {EventTarget~Event} 10474 */ 10475 this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true }); 10476 } 10477 }; 10478 10479 /** 10480 * Turn on listeners for {@link VideoTrackList}, {@link {AudioTrackList}, and 10481 * {@link TextTrackList} events. 10482 * 10483 * This adds {@link EventTarget~EventListeners} for `addtrack`, and `removetrack`. 10484 * 10485 * @fires Tech#audiotrackchange 10486 * @fires Tech#videotrackchange 10487 * @fires Tech#texttrackchange 10488 */ 10489 10490 10491 Tech.prototype.initTrackListeners = function initTrackListeners() { 10492 var _this3 = this; 10493 10494 /** 10495 * Triggered when tracks are added or removed on the Tech {@link AudioTrackList} 10496 * 10497 * @event Tech#audiotrackchange 10498 * @type {EventTarget~Event} 10499 */ 10500 10501 /** 10502 * Triggered when tracks are added or removed on the Tech {@link VideoTrackList} 10503 * 10504 * @event Tech#videotrackchange 10505 * @type {EventTarget~Event} 10506 */ 10507 10508 /** 10509 * Triggered when tracks are added or removed on the Tech {@link TextTrackList} 10510 * 10511 * @event Tech#texttrackchange 10512 * @type {EventTarget~Event} 10513 */ 10514 NORMAL.names.forEach(function (name) { 10515 var props = NORMAL[name]; 10516 var trackListChanges = function trackListChanges() { 10517 _this3.trigger(name + 'trackchange'); 10518 }; 10519 10520 var tracks = _this3[props.getterName](); 10521 10522 tracks.addEventListener('removetrack', trackListChanges); 10523 tracks.addEventListener('addtrack', trackListChanges); 10524 10525 _this3.on('dispose', function () { 10526 tracks.removeEventListener('removetrack', trackListChanges); 10527 tracks.removeEventListener('addtrack', trackListChanges); 10528 }); 10529 }); 10530 }; 10531 10532 /** 10533 * Emulate TextTracks using vtt.js if necessary 10534 * 10535 * @fires Tech#vttjsloaded 10536 * @fires Tech#vttjserror 10537 */ 10538 10539 10540 Tech.prototype.addWebVttScript_ = function addWebVttScript_() { 10541 var _this4 = this; 10542 10543 if (window_1.WebVTT) { 10544 return; 10545 } 10546 10547 // Initially, Tech.el_ is a child of a dummy-div wait until the Component system 10548 // signals that the Tech is ready at which point Tech.el_ is part of the DOM 10549 // before inserting the WebVTT script 10550 if (document_1.body.contains(this.el())) { 10551 10552 // load via require if available and vtt.js script location was not passed in 10553 // as an option. novtt builds will turn the above require call into an empty object 10554 // which will cause this if check to always fail. 10555 if (!this.options_['vtt.js'] && isPlain(browserIndex) && Object.keys(browserIndex).length > 0) { 10556 this.trigger('vttjsloaded'); 10557 return; 10558 } 10559 10560 // load vtt.js via the script location option or the cdn of no location was 10561 // passed in 10562 var script = document_1.createElement('script'); 10563 10564 script.src = this.options_['vtt.js'] || 'https://vjs.zencdn.net/vttjs/0.12.4/vtt.min.js'; 10565 script.onload = function () { 10566 /** 10567 * Fired when vtt.js is loaded. 10568 * 10569 * @event Tech#vttjsloaded 10570 * @type {EventTarget~Event} 10571 */ 10572 _this4.trigger('vttjsloaded'); 10573 }; 10574 script.onerror = function () { 10575 /** 10576 * Fired when vtt.js was not loaded due to an error 10577 * 10578 * @event Tech#vttjsloaded 10579 * @type {EventTarget~Event} 10580 */ 10581 _this4.trigger('vttjserror'); 10582 }; 10583 this.on('dispose', function () { 10584 script.onload = null; 10585 script.onerror = null; 10586 }); 10587 // but have not loaded yet and we set it to true before the inject so that 10588 // we don't overwrite the injected window.WebVTT if it loads right away 10589 window_1.WebVTT = true; 10590 this.el().parentNode.appendChild(script); 10591 } else { 10592 this.ready(this.addWebVttScript_); 10593 } 10594 }; 10595 10596 /** 10597 * Emulate texttracks 10598 * 10599 */ 10600 10601 10602 Tech.prototype.emulateTextTracks = function emulateTextTracks() { 10603 var _this5 = this; 10604 10605 var tracks = this.textTracks(); 10606 var remoteTracks = this.remoteTextTracks(); 10607 var handleAddTrack = function handleAddTrack(e) { 10608 return tracks.addTrack(e.track); 10609 }; 10610 var handleRemoveTrack = function handleRemoveTrack(e) { 10611 return tracks.removeTrack(e.track); 10612 }; 10613 10614 remoteTracks.on('addtrack', handleAddTrack); 10615 remoteTracks.on('removetrack', handleRemoveTrack); 10616 10617 this.addWebVttScript_(); 10618 10619 var updateDisplay = function updateDisplay() { 10620 return _this5.trigger('texttrackchange'); 10621 }; 10622 10623 var textTracksChanges = function textTracksChanges() { 10624 updateDisplay(); 10625 10626 for (var i = 0; i < tracks.length; i++) { 10627 var track = tracks[i]; 10628 10629 track.removeEventListener('cuechange', updateDisplay); 10630 if (track.mode === 'showing') { 10631 track.addEventListener('cuechange', updateDisplay); 10632 } 10633 } 10634 }; 10635 10636 textTracksChanges(); 10637 tracks.addEventListener('change', textTracksChanges); 10638 tracks.addEventListener('addtrack', textTracksChanges); 10639 tracks.addEventListener('removetrack', textTracksChanges); 10640 10641 this.on('dispose', function () { 10642 remoteTracks.off('addtrack', handleAddTrack); 10643 remoteTracks.off('removetrack', handleRemoveTrack); 10644 tracks.removeEventListener('change', textTracksChanges); 10645 tracks.removeEventListener('addtrack', textTracksChanges); 10646 tracks.removeEventListener('removetrack', textTracksChanges); 10647 10648 for (var i = 0; i < tracks.length; i++) { 10649 var track = tracks[i]; 10650 10651 track.removeEventListener('cuechange', updateDisplay); 10652 } 10653 }); 10654 }; 10655 10656 /** 10657 * Create and returns a remote {@link TextTrack} object. 10658 * 10659 * @param {string} kind 10660 * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata) 10661 * 10662 * @param {string} [label] 10663 * Label to identify the text track 10664 * 10665 * @param {string} [language] 10666 * Two letter language abbreviation 10667 * 10668 * @return {TextTrack} 10669 * The TextTrack that gets created. 10670 */ 10671 10672 10673 Tech.prototype.addTextTrack = function addTextTrack(kind, label, language) { 10674 if (!kind) { 10675 throw new Error('TextTrack kind is required but was not provided'); 10676 } 10677 10678 return createTrackHelper(this, kind, label, language); 10679 }; 10680 10681 /** 10682 * Create an emulated TextTrack for use by addRemoteTextTrack 10683 * 10684 * This is intended to be overridden by classes that inherit from 10685 * Tech in order to create native or custom TextTracks. 10686 * 10687 * @param {Object} options 10688 * The object should contain the options to initialize the TextTrack with. 10689 * 10690 * @param {string} [options.kind] 10691 * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata). 10692 * 10693 * @param {string} [options.label]. 10694 * Label to identify the text track 10695 * 10696 * @param {string} [options.language] 10697 * Two letter language abbreviation. 10698 * 10699 * @return {HTMLTrackElement} 10700 * The track element that gets created. 10701 */ 10702 10703 10704 Tech.prototype.createRemoteTextTrack = function createRemoteTextTrack(options) { 10705 var track = mergeOptions(options, { 10706 tech: this 10707 }); 10708 10709 return new REMOTE.remoteTextEl.TrackClass(track); 10710 }; 10711 10712 /** 10713 * Creates a remote text track object and returns an html track element. 10714 * 10715 * > Note: This can be an emulated {@link HTMLTrackElement} or a native one. 10716 * 10717 * @param {Object} options 10718 * See {@link Tech#createRemoteTextTrack} for more detailed properties. 10719 * 10720 * @param {boolean} [manualCleanup=true] 10721 * - When false: the TextTrack will be automatically removed from the video 10722 * element whenever the source changes 10723 * - When True: The TextTrack will have to be cleaned up manually 10724 * 10725 * @return {HTMLTrackElement} 10726 * An Html Track Element. 10727 * 10728 * @deprecated The default functionality for this function will be equivalent 10729 * to "manualCleanup=false" in the future. The manualCleanup parameter will 10730 * also be removed. 10731 */ 10732 10733 10734 Tech.prototype.addRemoteTextTrack = function addRemoteTextTrack() { 10735 var _this6 = this; 10736 10737 var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 10738 var manualCleanup = arguments[1]; 10739 10740 var htmlTrackElement = this.createRemoteTextTrack(options); 10741 10742 if (manualCleanup !== true && manualCleanup !== false) { 10743 // deprecation warning 10744 log$1.warn('Calling addRemoteTextTrack without explicitly setting the "manualCleanup" parameter to `true` is deprecated and default to `false` in future version of video.js'); 10745 manualCleanup = true; 10746 } 10747 10748 // store HTMLTrackElement and TextTrack to remote list 10749 this.remoteTextTrackEls().addTrackElement_(htmlTrackElement); 10750 this.remoteTextTracks().addTrack(htmlTrackElement.track); 10751 10752 if (manualCleanup !== true) { 10753 // create the TextTrackList if it doesn't exist 10754 this.ready(function () { 10755 return _this6.autoRemoteTextTracks_.addTrack(htmlTrackElement.track); 10756 }); 10757 } 10758 10759 return htmlTrackElement; 10760 }; 10761 10762 /** 10763 * Remove a remote text track from the remote `TextTrackList`. 10764 * 10765 * @param {TextTrack} track 10766 * `TextTrack` to remove from the `TextTrackList` 10767 */ 10768 10769 10770 Tech.prototype.removeRemoteTextTrack = function removeRemoteTextTrack(track) { 10771 var trackElement = this.remoteTextTrackEls().getTrackElementByTrack_(track); 10772 10773 // remove HTMLTrackElement and TextTrack from remote list 10774 this.remoteTextTrackEls().removeTrackElement_(trackElement); 10775 this.remoteTextTracks().removeTrack(track); 10776 this.autoRemoteTextTracks_.removeTrack(track); 10777 }; 10778 10779 /** 10780 * Gets available media playback quality metrics as specified by the W3C's Media 10781 * Playback Quality API. 10782 * 10783 * @see [Spec]{@link https://wicg.github.io/media-playback-quality} 10784 * 10785 * @return {Object} 10786 * An object with supported media playback quality metrics 10787 * 10788 * @abstract 10789 */ 10790 10791 10792 Tech.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() { 10793 return {}; 10794 }; 10795 10796 /** 10797 * A method to set a poster from a `Tech`. 10798 * 10799 * @abstract 10800 */ 10801 10802 10803 Tech.prototype.setPoster = function setPoster() {}; 10804 10805 /** 10806 * A method to check for the presence of the 'playsinine' <video> attribute. 10807 * 10808 * @abstract 10809 */ 10810 10811 10812 Tech.prototype.playsinline = function playsinline() {}; 10813 10814 /** 10815 * A method to set or unset the 'playsinine' <video> attribute. 10816 * 10817 * @abstract 10818 */ 10819 10820 10821 Tech.prototype.setPlaysinline = function setPlaysinline() {}; 10822 10823 /* 10824 * Check if the tech can support the given mime-type. 10825 * 10826 * The base tech does not support any type, but source handlers might 10827 * overwrite this. 10828 * 10829 * @param {string} type 10830 * The mimetype to check for support 10831 * 10832 * @return {string} 10833 * 'probably', 'maybe', or empty string 10834 * 10835 * @see [Spec]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canPlayType} 10836 * 10837 * @abstract 10838 */ 10839 10840 10841 Tech.prototype.canPlayType = function canPlayType() { 10842 return ''; 10843 }; 10844 10845 /** 10846 * Check if the type is supported by this tech. 10847 * 10848 * The base tech does not support any type, but source handlers might 10849 * overwrite this. 10850 * 10851 * @param {string} type 10852 * The media type to check 10853 * @return {string} Returns the native video element's response 10854 */ 10855 10856 10857 Tech.canPlayType = function canPlayType() { 10858 return ''; 10859 }; 10860 10861 /** 10862 * Check if the tech can support the given source 10863 * @param {Object} srcObj 10864 * The source object 10865 * @param {Object} options 10866 * The options passed to the tech 10867 * @return {string} 'probably', 'maybe', or '' (empty string) 10868 */ 10869 10870 10871 Tech.canPlaySource = function canPlaySource(srcObj, options) { 10872 return Tech.canPlayType(srcObj.type); 10873 }; 10874 10875 /* 10876 * Return whether the argument is a Tech or not. 10877 * Can be passed either a Class like `Html5` or a instance like `player.tech_` 10878 * 10879 * @param {Object} component 10880 * The item to check 10881 * 10882 * @return {boolean} 10883 * Whether it is a tech or not 10884 * - True if it is a tech 10885 * - False if it is not 10886 */ 10887 10888 10889 Tech.isTech = function isTech(component) { 10890 return component.prototype instanceof Tech || component instanceof Tech || component === Tech; 10891 }; 10892 10893 /** 10894 * Registers a `Tech` into a shared list for videojs. 10895 * 10896 * @param {string} name 10897 * Name of the `Tech` to register. 10898 * 10899 * @param {Object} tech 10900 * The `Tech` class to register. 10901 */ 10902 10903 10904 Tech.registerTech = function registerTech(name, tech) { 10905 if (!Tech.techs_) { 10906 Tech.techs_ = {}; 10907 } 10908 10909 if (!Tech.isTech(tech)) { 10910 throw new Error('Tech ' + name + ' must be a Tech'); 10911 } 10912 10913 if (!Tech.canPlayType) { 10914 throw new Error('Techs must have a static canPlayType method on them'); 10915 } 10916 if (!Tech.canPlaySource) { 10917 throw new Error('Techs must have a static canPlaySource method on them'); 10918 } 10919 10920 name = toTitleCase(name); 10921 10922 Tech.techs_[name] = tech; 10923 if (name !== 'Tech') { 10924 // camel case the techName for use in techOrder 10925 Tech.defaultTechOrder_.push(name); 10926 } 10927 return tech; 10928 }; 10929 10930 /** 10931 * Get a `Tech` from the shared list by name. 10932 * 10933 * @param {string} name 10934 * `camelCase` or `TitleCase` name of the Tech to get 10935 * 10936 * @return {Tech|undefined} 10937 * The `Tech` or undefined if there was no tech with the name requsted. 10938 */ 10939 10940 10941 Tech.getTech = function getTech(name) { 10942 if (!name) { 10943 return; 10944 } 10945 10946 name = toTitleCase(name); 10947 10948 if (Tech.techs_ && Tech.techs_[name]) { 10949 return Tech.techs_[name]; 10950 } 10951 10952 if (window_1 && window_1.videojs && window_1.videojs[name]) { 10953 log$1.warn('The ' + name + ' tech was added to the videojs object when it should be registered using videojs.registerTech(name, tech)'); 10954 return window_1.videojs[name]; 10955 } 10956 }; 10957 10958 return Tech; 10959}(Component); 10960 10961/** 10962 * Get the {@link VideoTrackList} 10963 * 10964 * @returns {VideoTrackList} 10965 * @method Tech.prototype.videoTracks 10966 */ 10967 10968/** 10969 * Get the {@link AudioTrackList} 10970 * 10971 * @returns {AudioTrackList} 10972 * @method Tech.prototype.audioTracks 10973 */ 10974 10975/** 10976 * Get the {@link TextTrackList} 10977 * 10978 * @returns {TextTrackList} 10979 * @method Tech.prototype.textTracks 10980 */ 10981 10982/** 10983 * Get the remote element {@link TextTrackList} 10984 * 10985 * @returns {TextTrackList} 10986 * @method Tech.prototype.remoteTextTracks 10987 */ 10988 10989/** 10990 * Get the remote element {@link HtmlTrackElementList} 10991 * 10992 * @returns {HtmlTrackElementList} 10993 * @method Tech.prototype.remoteTextTrackEls 10994 */ 10995 10996ALL.names.forEach(function (name) { 10997 var props = ALL[name]; 10998 10999 Tech.prototype[props.getterName] = function () { 11000 this[props.privateName] = this[props.privateName] || new props.ListClass(); 11001 return this[props.privateName]; 11002 }; 11003}); 11004 11005/** 11006 * List of associated text tracks 11007 * 11008 * @type {TextTrackList} 11009 * @private 11010 * @property Tech#textTracks_ 11011 */ 11012 11013/** 11014 * List of associated audio tracks. 11015 * 11016 * @type {AudioTrackList} 11017 * @private 11018 * @property Tech#audioTracks_ 11019 */ 11020 11021/** 11022 * List of associated video tracks. 11023 * 11024 * @type {VideoTrackList} 11025 * @private 11026 * @property Tech#videoTracks_ 11027 */ 11028 11029/** 11030 * Boolean indicating wether the `Tech` supports volume control. 11031 * 11032 * @type {boolean} 11033 * @default 11034 */ 11035Tech.prototype.featuresVolumeControl = true; 11036 11037/** 11038 * Boolean indicating wether the `Tech` support fullscreen resize control. 11039 * Resizing plugins using request fullscreen reloads the plugin 11040 * 11041 * @type {boolean} 11042 * @default 11043 */ 11044Tech.prototype.featuresFullscreenResize = false; 11045 11046/** 11047 * Boolean indicating wether the `Tech` supports changing the speed at which the video 11048 * plays. Examples: 11049 * - Set player to play 2x (twice) as fast 11050 * - Set player to play 0.5x (half) as fast 11051 * 11052 * @type {boolean} 11053 * @default 11054 */ 11055Tech.prototype.featuresPlaybackRate = false; 11056 11057/** 11058 * Boolean indicating wether the `Tech` supports the `progress` event. This is currently 11059 * not triggered by video-js-swf. This will be used to determine if 11060 * {@link Tech#manualProgressOn} should be called. 11061 * 11062 * @type {boolean} 11063 * @default 11064 */ 11065Tech.prototype.featuresProgressEvents = false; 11066 11067/** 11068 * Boolean indicating wether the `Tech` supports the `timeupdate` event. This is currently 11069 * not triggered by video-js-swf. This will be used to determine if 11070 * {@link Tech#manualTimeUpdates} should be called. 11071 * 11072 * @type {boolean} 11073 * @default 11074 */ 11075Tech.prototype.featuresTimeupdateEvents = false; 11076 11077/** 11078 * Boolean indicating wether the `Tech` supports the native `TextTrack`s. 11079 * This will help us integrate with native `TextTrack`s if the browser supports them. 11080 * 11081 * @type {boolean} 11082 * @default 11083 */ 11084Tech.prototype.featuresNativeTextTracks = false; 11085 11086/** 11087 * A functional mixin for techs that want to use the Source Handler pattern. 11088 * Source handlers are scripts for handling specific formats. 11089 * The source handler pattern is used for adaptive formats (HLS, DASH) that 11090 * manually load video data and feed it into a Source Buffer (Media Source Extensions) 11091 * Example: `Tech.withSourceHandlers.call(MyTech);` 11092 * 11093 * @param {Tech} _Tech 11094 * The tech to add source handler functions to. 11095 * 11096 * @mixes Tech~SourceHandlerAdditions 11097 */ 11098Tech.withSourceHandlers = function (_Tech) { 11099 11100 /** 11101 * Register a source handler 11102 * 11103 * @param {Function} handler 11104 * The source handler class 11105 * 11106 * @param {number} [index] 11107 * Register it at the following index 11108 */ 11109 _Tech.registerSourceHandler = function (handler, index) { 11110 var handlers = _Tech.sourceHandlers; 11111 11112 if (!handlers) { 11113 handlers = _Tech.sourceHandlers = []; 11114 } 11115 11116 if (index === undefined) { 11117 // add to the end of the list 11118 index = handlers.length; 11119 } 11120 11121 handlers.splice(index, 0, handler); 11122 }; 11123 11124 /** 11125 * Check if the tech can support the given type. Also checks the 11126 * Techs sourceHandlers. 11127 * 11128 * @param {string} type 11129 * The mimetype to check. 11130 * 11131 * @return {string} 11132 * 'probably', 'maybe', or '' (empty string) 11133 */ 11134 _Tech.canPlayType = function (type) { 11135 var handlers = _Tech.sourceHandlers || []; 11136 var can = void 0; 11137 11138 for (var i = 0; i < handlers.length; i++) { 11139 can = handlers[i].canPlayType(type); 11140 11141 if (can) { 11142 return can; 11143 } 11144 } 11145 11146 return ''; 11147 }; 11148 11149 /** 11150 * Returns the first source handler that supports the source. 11151 * 11152 * TODO: Answer question: should 'probably' be prioritized over 'maybe' 11153 * 11154 * @param {Tech~SourceObject} source 11155 * The source object 11156 * 11157 * @param {Object} options 11158 * The options passed to the tech 11159 * 11160 * @return {SourceHandler|null} 11161 * The first source handler that supports the source or null if 11162 * no SourceHandler supports the source 11163 */ 11164 _Tech.selectSourceHandler = function (source, options) { 11165 var handlers = _Tech.sourceHandlers || []; 11166 var can = void 0; 11167 11168 for (var i = 0; i < handlers.length; i++) { 11169 can = handlers[i].canHandleSource(source, options); 11170 11171 if (can) { 11172 return handlers[i]; 11173 } 11174 } 11175 11176 return null; 11177 }; 11178 11179 /** 11180 * Check if the tech can support the given source. 11181 * 11182 * @param {Tech~SourceObject} srcObj 11183 * The source object 11184 * 11185 * @param {Object} options 11186 * The options passed to the tech 11187 * 11188 * @return {string} 11189 * 'probably', 'maybe', or '' (empty string) 11190 */ 11191 _Tech.canPlaySource = function (srcObj, options) { 11192 var sh = _Tech.selectSourceHandler(srcObj, options); 11193 11194 if (sh) { 11195 return sh.canHandleSource(srcObj, options); 11196 } 11197 11198 return ''; 11199 }; 11200 11201 /** 11202 * When using a source handler, prefer its implementation of 11203 * any function normally provided by the tech. 11204 */ 11205 var deferrable = ['seekable', 'duration']; 11206 11207 /** 11208 * A wrapper around {@link Tech#seekable} that will call a `SourceHandler`s seekable 11209 * function if it exists, with a fallback to the Techs seekable function. 11210 * 11211 * @method _Tech.seekable 11212 */ 11213 11214 /** 11215 * A wrapper around {@link Tech#duration} that will call a `SourceHandler`s duration 11216 * function if it exists, otherwise it will fallback to the techs duration function. 11217 * 11218 * @method _Tech.duration 11219 */ 11220 11221 deferrable.forEach(function (fnName) { 11222 var originalFn = this[fnName]; 11223 11224 if (typeof originalFn !== 'function') { 11225 return; 11226 } 11227 11228 this[fnName] = function () { 11229 if (this.sourceHandler_ && this.sourceHandler_[fnName]) { 11230 return this.sourceHandler_[fnName].apply(this.sourceHandler_, arguments); 11231 } 11232 return originalFn.apply(this, arguments); 11233 }; 11234 }, _Tech.prototype); 11235 11236 /** 11237 * Create a function for setting the source using a source object 11238 * and source handlers. 11239 * Should never be called unless a source handler was found. 11240 * 11241 * @param {Tech~SourceObject} source 11242 * A source object with src and type keys 11243 */ 11244 _Tech.prototype.setSource = function (source) { 11245 var sh = _Tech.selectSourceHandler(source, this.options_); 11246 11247 if (!sh) { 11248 // Fall back to a native source hander when unsupported sources are 11249 // deliberately set 11250 if (_Tech.nativeSourceHandler) { 11251 sh = _Tech.nativeSourceHandler; 11252 } else { 11253 log$1.error('No source hander found for the current source.'); 11254 } 11255 } 11256 11257 // Dispose any existing source handler 11258 this.disposeSourceHandler(); 11259 this.off('dispose', this.disposeSourceHandler); 11260 11261 if (sh !== _Tech.nativeSourceHandler) { 11262 this.currentSource_ = source; 11263 } 11264 11265 this.sourceHandler_ = sh.handleSource(source, this, this.options_); 11266 this.on('dispose', this.disposeSourceHandler); 11267 }; 11268 11269 /** 11270 * Clean up any existing SourceHandlers and listeners when the Tech is disposed. 11271 * 11272 * @listens Tech#dispose 11273 */ 11274 _Tech.prototype.disposeSourceHandler = function () { 11275 // if we have a source and get another one 11276 // then we are loading something new 11277 // than clear all of our current tracks 11278 if (this.currentSource_) { 11279 this.clearTracks(['audio', 'video']); 11280 this.currentSource_ = null; 11281 } 11282 11283 // always clean up auto-text tracks 11284 this.cleanupAutoTextTracks(); 11285 11286 if (this.sourceHandler_) { 11287 11288 if (this.sourceHandler_.dispose) { 11289 this.sourceHandler_.dispose(); 11290 } 11291 11292 this.sourceHandler_ = null; 11293 } 11294 }; 11295}; 11296 11297// The base Tech class needs to be registered as a Component. It is the only 11298// Tech that can be registered as a Component. 11299Component.registerComponent('Tech', Tech); 11300Tech.registerTech('Tech', Tech); 11301 11302/** 11303 * A list of techs that should be added to techOrder on Players 11304 * 11305 * @private 11306 */ 11307Tech.defaultTechOrder_ = []; 11308 11309var middlewares = {}; 11310 11311function use(type, middleware) { 11312 middlewares[type] = middlewares[type] || []; 11313 middlewares[type].push(middleware); 11314} 11315 11316 11317 11318function setSource(player, src, next) { 11319 player.setTimeout(function () { 11320 return setSourceHelper(src, middlewares[src.type], next, player); 11321 }, 1); 11322} 11323 11324function setTech(middleware, tech) { 11325 middleware.forEach(function (mw) { 11326 return mw.setTech && mw.setTech(tech); 11327 }); 11328} 11329 11330function get$1(middleware, tech, method) { 11331 return middleware.reduceRight(middlewareIterator(method), tech[method]()); 11332} 11333 11334function set$1(middleware, tech, method, arg) { 11335 return tech[method](middleware.reduce(middlewareIterator(method), arg)); 11336} 11337 11338var allowedGetters = { 11339 buffered: 1, 11340 currentTime: 1, 11341 duration: 1, 11342 seekable: 1, 11343 played: 1 11344}; 11345 11346var allowedSetters = { 11347 setCurrentTime: 1 11348}; 11349 11350function middlewareIterator(method) { 11351 return function (value, mw) { 11352 if (mw[method]) { 11353 return mw[method](value); 11354 } 11355 11356 return value; 11357 }; 11358} 11359 11360function setSourceHelper() { 11361 var src = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 11362 var middleware = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; 11363 var next = arguments[2]; 11364 var player = arguments[3]; 11365 var acc = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : []; 11366 var lastRun = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false; 11367 var mwFactory = middleware[0], 11368 mwrest = middleware.slice(1); 11369 11370 // if mwFactory is a string, then we're at a fork in the road 11371 11372 if (typeof mwFactory === 'string') { 11373 setSourceHelper(src, middlewares[mwFactory], next, player, acc, lastRun); 11374 11375 // if we have an mwFactory, call it with the player to get the mw, 11376 // then call the mw's setSource method 11377 } else if (mwFactory) { 11378 var mw = mwFactory(player); 11379 11380 mw.setSource(assign({}, src), function (err, _src) { 11381 11382 // something happened, try the next middleware on the current level 11383 // make sure to use the old src 11384 if (err) { 11385 return setSourceHelper(src, mwrest, next, player, acc, lastRun); 11386 } 11387 11388 // we've succeeded, now we need to go deeper 11389 acc.push(mw); 11390 11391 // if it's the same type, continue down the current chain 11392 // otherwise, we want to go down the new chain 11393 setSourceHelper(_src, src.type === _src.type ? mwrest : middlewares[_src.type], next, player, acc, lastRun); 11394 }); 11395 } else if (mwrest.length) { 11396 setSourceHelper(src, mwrest, next, player, acc, lastRun); 11397 } else if (lastRun) { 11398 next(src, acc); 11399 } else { 11400 setSourceHelper(src, middlewares['*'], next, player, acc, true); 11401 } 11402} 11403 11404/** 11405 * @module filter-source 11406 */ 11407/** 11408 * Filter out single bad source objects or multiple source objects in an 11409 * array. Also flattens nested source object arrays into a 1 dimensional 11410 * array of source objects. 11411 * 11412 * @param {Tech~SourceObject|Tech~SourceObject[]} src 11413 * The src object to filter 11414 * 11415 * @return {Tech~SourceObject[]} 11416 * An array of sourceobjects containing only valid sources 11417 * 11418 * @private 11419 */ 11420var filterSource = function filterSource(src) { 11421 // traverse array 11422 if (Array.isArray(src)) { 11423 var newsrc = []; 11424 11425 src.forEach(function (srcobj) { 11426 srcobj = filterSource(srcobj); 11427 11428 if (Array.isArray(srcobj)) { 11429 newsrc = newsrc.concat(srcobj); 11430 } else if (isObject(srcobj)) { 11431 newsrc.push(srcobj); 11432 } 11433 }); 11434 11435 src = newsrc; 11436 } else if (typeof src === 'string' && src.trim()) { 11437 // convert string into object 11438 src = [{ src: src }]; 11439 } else if (isObject(src) && typeof src.src === 'string' && src.src && src.src.trim()) { 11440 // src is already valid 11441 src = [src]; 11442 } else { 11443 // invalid source, turn it into an empty array 11444 src = []; 11445 } 11446 11447 return src; 11448}; 11449 11450/** 11451 * @file loader.js 11452 */ 11453/** 11454 * The `MediaLoader` is the `Component` that decides which playback technology to load 11455 * when a player is initialized. 11456 * 11457 * @extends Component 11458 */ 11459 11460var MediaLoader = function (_Component) { 11461 inherits(MediaLoader, _Component); 11462 11463 /** 11464 * Create an instance of this class. 11465 * 11466 * @param {Player} player 11467 * The `Player` that this class should attach to. 11468 * 11469 * @param {Object} [options] 11470 * The key/value stroe of player options. 11471 * 11472 * @param {Component~ReadyCallback} [ready] 11473 * The function that is run when this component is ready. 11474 */ 11475 function MediaLoader(player, options, ready) { 11476 classCallCheck(this, MediaLoader); 11477 11478 // MediaLoader has no element 11479 var options_ = mergeOptions({ createEl: false }, options); 11480 11481 // If there are no sources when the player is initialized, 11482 // load the first supported playback technology. 11483 11484 var _this = possibleConstructorReturn(this, _Component.call(this, player, options_, ready)); 11485 11486 if (!options.playerOptions.sources || options.playerOptions.sources.length === 0) { 11487 for (var i = 0, j = options.playerOptions.techOrder; i < j.length; i++) { 11488 var techName = toTitleCase(j[i]); 11489 var tech = Tech.getTech(techName); 11490 11491 // Support old behavior of techs being registered as components. 11492 // Remove once that deprecated behavior is removed. 11493 if (!techName) { 11494 tech = Component.getComponent(techName); 11495 } 11496 11497 // Check if the browser supports this technology 11498 if (tech && tech.isSupported()) { 11499 player.loadTech_(techName); 11500 break; 11501 } 11502 } 11503 } else { 11504 // Loop through playback technologies (HTML5, Flash) and check for support. 11505 // Then load the best source. 11506 // A few assumptions here: 11507 // All playback technologies respect preload false. 11508 player.src(options.playerOptions.sources); 11509 } 11510 return _this; 11511 } 11512 11513 return MediaLoader; 11514}(Component); 11515 11516Component.registerComponent('MediaLoader', MediaLoader); 11517 11518/** 11519 * @file button.js 11520 */ 11521/** 11522 * Clickable Component which is clickable or keyboard actionable, 11523 * but is not a native HTML button. 11524 * 11525 * @extends Component 11526 */ 11527 11528var ClickableComponent = function (_Component) { 11529 inherits(ClickableComponent, _Component); 11530 11531 /** 11532 * Creates an instance of this class. 11533 * 11534 * @param {Player} player 11535 * The `Player` that this class should be attached to. 11536 * 11537 * @param {Object} [options] 11538 * The key/value store of player options. 11539 */ 11540 function ClickableComponent(player, options) { 11541 classCallCheck(this, ClickableComponent); 11542 11543 var _this = possibleConstructorReturn(this, _Component.call(this, player, options)); 11544 11545 _this.emitTapEvents(); 11546 11547 _this.enable(); 11548 return _this; 11549 } 11550 11551 /** 11552 * Create the `Component`s DOM element. 11553 * 11554 * @param {string} [tag=div] 11555 * The element's node type. 11556 * 11557 * @param {Object} [props={}] 11558 * An object of properties that should be set on the element. 11559 * 11560 * @param {Object} [attributes={}] 11561 * An object of attributes that should be set on the element. 11562 * 11563 * @return {Element} 11564 * The element that gets created. 11565 */ 11566 11567 11568 ClickableComponent.prototype.createEl = function createEl$$1() { 11569 var tag = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'div'; 11570 var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 11571 var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; 11572 11573 props = assign({ 11574 innerHTML: '<span aria-hidden="true" class="vjs-icon-placeholder"></span>', 11575 className: this.buildCSSClass(), 11576 tabIndex: 0 11577 }, props); 11578 11579 if (tag === 'button') { 11580 log$1.error('Creating a ClickableComponent with an HTML element of ' + tag + ' is not supported; use a Button instead.'); 11581 } 11582 11583 // Add ARIA attributes for clickable element which is not a native HTML button 11584 attributes = assign({ 11585 'role': 'button', 11586 11587 // let the screen reader user know that the text of the element may change 11588 'aria-live': 'polite' 11589 }, attributes); 11590 11591 this.tabIndex_ = props.tabIndex; 11592 11593 var el = _Component.prototype.createEl.call(this, tag, props, attributes); 11594 11595 this.createControlTextEl(el); 11596 11597 return el; 11598 }; 11599 11600 ClickableComponent.prototype.dispose = function dispose() { 11601 // remove controlTextEl_ on dipose 11602 this.controlTextEl_ = null; 11603 11604 _Component.prototype.dispose.call(this); 11605 }; 11606 11607 /** 11608 * Create a control text element on this `Component` 11609 * 11610 * @param {Element} [el] 11611 * Parent element for the control text. 11612 * 11613 * @return {Element} 11614 * The control text element that gets created. 11615 */ 11616 11617 11618 ClickableComponent.prototype.createControlTextEl = function createControlTextEl(el) { 11619 this.controlTextEl_ = createEl('span', { 11620 className: 'vjs-control-text' 11621 }); 11622 11623 if (el) { 11624 el.appendChild(this.controlTextEl_); 11625 } 11626 11627 this.controlText(this.controlText_, el); 11628 11629 return this.controlTextEl_; 11630 }; 11631 11632 /** 11633 * Get or set the localize text to use for the controls on the `Component`. 11634 * 11635 * @param {string} [text] 11636 * Control text for element. 11637 * 11638 * @param {Element} [el=this.el()] 11639 * Element to set the title on. 11640 * 11641 * @return {string} 11642 * - The control text when getting 11643 */ 11644 11645 11646 ClickableComponent.prototype.controlText = function controlText(text) { 11647 var el = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.el(); 11648 11649 if (text === undefined) { 11650 return this.controlText_ || 'Need Text'; 11651 } 11652 11653 var localizedText = this.localize(text); 11654 11655 this.controlText_ = text; 11656 textContent(this.controlTextEl_, localizedText); 11657 if (!this.nonIconControl) { 11658 // Set title attribute if only an icon is shown 11659 el.setAttribute('title', localizedText); 11660 } 11661 }; 11662 11663 /** 11664 * Builds the default DOM `className`. 11665 * 11666 * @return {string} 11667 * The DOM `className` for this object. 11668 */ 11669 11670 11671 ClickableComponent.prototype.buildCSSClass = function buildCSSClass() { 11672 return 'vjs-control vjs-button ' + _Component.prototype.buildCSSClass.call(this); 11673 }; 11674 11675 /** 11676 * Enable this `Component`s element. 11677 */ 11678 11679 11680 ClickableComponent.prototype.enable = function enable() { 11681 if (!this.enabled_) { 11682 this.enabled_ = true; 11683 this.removeClass('vjs-disabled'); 11684 this.el_.setAttribute('aria-disabled', 'false'); 11685 if (typeof this.tabIndex_ !== 'undefined') { 11686 this.el_.setAttribute('tabIndex', this.tabIndex_); 11687 } 11688 this.on(['tap', 'click'], this.handleClick); 11689 this.on('focus', this.handleFocus); 11690 this.on('blur', this.handleBlur); 11691 } 11692 }; 11693 11694 /** 11695 * Disable this `Component`s element. 11696 */ 11697 11698 11699 ClickableComponent.prototype.disable = function disable() { 11700 this.enabled_ = false; 11701 this.addClass('vjs-disabled'); 11702 this.el_.setAttribute('aria-disabled', 'true'); 11703 if (typeof this.tabIndex_ !== 'undefined') { 11704 this.el_.removeAttribute('tabIndex'); 11705 } 11706 this.off(['tap', 'click'], this.handleClick); 11707 this.off('focus', this.handleFocus); 11708 this.off('blur', this.handleBlur); 11709 }; 11710 11711 /** 11712 * This gets called when a `ClickableComponent` gets: 11713 * - Clicked (via the `click` event, listening starts in the constructor) 11714 * - Tapped (via the `tap` event, listening starts in the constructor) 11715 * - The following things happen in order: 11716 * 1. {@link ClickableComponent#handleFocus} is called via a `focus` event on the 11717 * `ClickableComponent`. 11718 * 2. {@link ClickableComponent#handleFocus} adds a listener for `keydown` on using 11719 * {@link ClickableComponent#handleKeyPress}. 11720 * 3. `ClickableComponent` has not had a `blur` event (`blur` means that focus was lost). The user presses 11721 * the space or enter key. 11722 * 4. {@link ClickableComponent#handleKeyPress} calls this function with the `keydown` 11723 * event as a parameter. 11724 * 11725 * @param {EventTarget~Event} event 11726 * The `keydown`, `tap`, or `click` event that caused this function to be 11727 * called. 11728 * 11729 * @listens tap 11730 * @listens click 11731 * @abstract 11732 */ 11733 11734 11735 ClickableComponent.prototype.handleClick = function handleClick(event) {}; 11736 11737 /** 11738 * This gets called when a `ClickableComponent` gains focus via a `focus` event. 11739 * Turns on listening for `keydown` events. When they happen it 11740 * calls `this.handleKeyPress`. 11741 * 11742 * @param {EventTarget~Event} event 11743 * The `focus` event that caused this function to be called. 11744 * 11745 * @listens focus 11746 */ 11747 11748 11749 ClickableComponent.prototype.handleFocus = function handleFocus(event) { 11750 on(document_1, 'keydown', bind(this, this.handleKeyPress)); 11751 }; 11752 11753 /** 11754 * Called when this ClickableComponent has focus and a key gets pressed down. By 11755 * default it will call `this.handleClick` when the key is space or enter. 11756 * 11757 * @param {EventTarget~Event} event 11758 * The `keydown` event that caused this function to be called. 11759 * 11760 * @listens keydown 11761 */ 11762 11763 11764 ClickableComponent.prototype.handleKeyPress = function handleKeyPress(event) { 11765 11766 // Support Space (32) or Enter (13) key operation to fire a click event 11767 if (event.which === 32 || event.which === 13) { 11768 event.preventDefault(); 11769 this.trigger('click'); 11770 } else if (_Component.prototype.handleKeyPress) { 11771 11772 // Pass keypress handling up for unsupported keys 11773 _Component.prototype.handleKeyPress.call(this, event); 11774 } 11775 }; 11776 11777 /** 11778 * Called when a `ClickableComponent` loses focus. Turns off the listener for 11779 * `keydown` events. Which Stops `this.handleKeyPress` from getting called. 11780 * 11781 * @param {EventTarget~Event} event 11782 * The `blur` event that caused this function to be called. 11783 * 11784 * @listens blur 11785 */ 11786 11787 11788 ClickableComponent.prototype.handleBlur = function handleBlur(event) { 11789 off(document_1, 'keydown', bind(this, this.handleKeyPress)); 11790 }; 11791 11792 return ClickableComponent; 11793}(Component); 11794 11795Component.registerComponent('ClickableComponent', ClickableComponent); 11796 11797/** 11798 * @file poster-image.js 11799 */ 11800/** 11801 * A `ClickableComponent` that handles showing the poster image for the player. 11802 * 11803 * @extends ClickableComponent 11804 */ 11805 11806var PosterImage = function (_ClickableComponent) { 11807 inherits(PosterImage, _ClickableComponent); 11808 11809 /** 11810 * Create an instance of this class. 11811 * 11812 * @param {Player} player 11813 * The `Player` that this class should attach to. 11814 * 11815 * @param {Object} [options] 11816 * The key/value store of player options. 11817 */ 11818 function PosterImage(player, options) { 11819 classCallCheck(this, PosterImage); 11820 11821 var _this = possibleConstructorReturn(this, _ClickableComponent.call(this, player, options)); 11822 11823 _this.update(); 11824 player.on('posterchange', bind(_this, _this.update)); 11825 return _this; 11826 } 11827 11828 /** 11829 * Clean up and dispose of the `PosterImage`. 11830 */ 11831 11832 11833 PosterImage.prototype.dispose = function dispose() { 11834 this.player().off('posterchange', this.update); 11835 _ClickableComponent.prototype.dispose.call(this); 11836 }; 11837 11838 /** 11839 * Create the `PosterImage`s DOM element. 11840 * 11841 * @return {Element} 11842 * The element that gets created. 11843 */ 11844 11845 11846 PosterImage.prototype.createEl = function createEl$$1() { 11847 var el = createEl('div', { 11848 className: 'vjs-poster', 11849 11850 // Don't want poster to be tabbable. 11851 tabIndex: -1 11852 }); 11853 11854 // To ensure the poster image resizes while maintaining its original aspect 11855 // ratio, use a div with `background-size` when available. For browsers that 11856 // do not support `background-size` (e.g. IE8), fall back on using a regular 11857 // img element. 11858 if (!BACKGROUND_SIZE_SUPPORTED) { 11859 this.fallbackImg_ = createEl('img'); 11860 el.appendChild(this.fallbackImg_); 11861 } 11862 11863 return el; 11864 }; 11865 11866 /** 11867 * An {@link EventTarget~EventListener} for {@link Player#posterchange} events. 11868 * 11869 * @listens Player#posterchange 11870 * 11871 * @param {EventTarget~Event} [event] 11872 * The `Player#posterchange` event that triggered this function. 11873 */ 11874 11875 11876 PosterImage.prototype.update = function update(event) { 11877 var url = this.player().poster(); 11878 11879 this.setSrc(url); 11880 11881 // If there's no poster source we should display:none on this component 11882 // so it's not still clickable or right-clickable 11883 if (url) { 11884 this.show(); 11885 } else { 11886 this.hide(); 11887 } 11888 }; 11889 11890 /** 11891 * Set the source of the `PosterImage` depending on the display method. 11892 * 11893 * @param {string} url 11894 * The URL to the source for the `PosterImage`. 11895 */ 11896 11897 11898 PosterImage.prototype.setSrc = function setSrc(url) { 11899 if (this.fallbackImg_) { 11900 this.fallbackImg_.src = url; 11901 } else { 11902 var backgroundImage = ''; 11903 11904 // Any falsey values should stay as an empty string, otherwise 11905 // this will throw an extra error 11906 if (url) { 11907 backgroundImage = 'url("' + url + '")'; 11908 } 11909 11910 this.el_.style.backgroundImage = backgroundImage; 11911 } 11912 }; 11913 11914 /** 11915 * An {@link EventTarget~EventListener} for clicks on the `PosterImage`. See 11916 * {@link ClickableComponent#handleClick} for instances where this will be triggered. 11917 * 11918 * @listens tap 11919 * @listens click 11920 * @listens keydown 11921 * 11922 * @param {EventTarget~Event} event 11923 + The `click`, `tap` or `keydown` event that caused this function to be called. 11924 */ 11925 11926 11927 PosterImage.prototype.handleClick = function handleClick(event) { 11928 // We don't want a click to trigger playback when controls are disabled 11929 if (!this.player_.controls()) { 11930 return; 11931 } 11932 11933 if (this.player_.paused()) { 11934 this.player_.play(); 11935 } else { 11936 this.player_.pause(); 11937 } 11938 }; 11939 11940 return PosterImage; 11941}(ClickableComponent); 11942 11943Component.registerComponent('PosterImage', PosterImage); 11944 11945/** 11946 * @file text-track-display.js 11947 */ 11948var darkGray = '#222'; 11949var lightGray = '#ccc'; 11950var fontMap = { 11951 monospace: 'monospace', 11952 sansSerif: 'sans-serif', 11953 serif: 'serif', 11954 monospaceSansSerif: '"Andale Mono", "Lucida Console", monospace', 11955 monospaceSerif: '"Courier New", monospace', 11956 proportionalSansSerif: 'sans-serif', 11957 proportionalSerif: 'serif', 11958 casual: '"Comic Sans MS", Impact, fantasy', 11959 script: '"Monotype Corsiva", cursive', 11960 smallcaps: '"Andale Mono", "Lucida Console", monospace, sans-serif' 11961}; 11962 11963/** 11964 * Construct an rgba color from a given hex color code. 11965 * 11966 * @param {number} color 11967 * Hex number for color, like #f0e. 11968 * 11969 * @param {number} opacity 11970 * Value for opacity, 0.0 - 1.0. 11971 * 11972 * @return {string} 11973 * The rgba color that was created, like 'rgba(255, 0, 0, 0.3)'. 11974 * 11975 * @private 11976 */ 11977function constructColor(color, opacity) { 11978 return 'rgba(' + 11979 // color looks like "#f0e" 11980 parseInt(color[1] + color[1], 16) + ',' + parseInt(color[2] + color[2], 16) + ',' + parseInt(color[3] + color[3], 16) + ',' + opacity + ')'; 11981} 11982 11983/** 11984 * Try to update the style of a DOM element. Some style changes will throw an error, 11985 * particularly in IE8. Those should be noops. 11986 * 11987 * @param {Element} el 11988 * The DOM element to be styled. 11989 * 11990 * @param {string} style 11991 * The CSS property on the element that should be styled. 11992 * 11993 * @param {string} rule 11994 * The style rule that should be applied to the property. 11995 * 11996 * @private 11997 */ 11998function tryUpdateStyle(el, style, rule) { 11999 try { 12000 el.style[style] = rule; 12001 } catch (e) { 12002 12003 // Satisfies linter. 12004 return; 12005 } 12006} 12007 12008/** 12009 * The component for displaying text track cues. 12010 * 12011 * @extends Component 12012 */ 12013 12014var TextTrackDisplay = function (_Component) { 12015 inherits(TextTrackDisplay, _Component); 12016 12017 /** 12018 * Creates an instance of this class. 12019 * 12020 * @param {Player} player 12021 * The `Player` that this class should be attached to. 12022 * 12023 * @param {Object} [options] 12024 * The key/value store of player options. 12025 * 12026 * @param {Component~ReadyCallback} [ready] 12027 * The function to call when `TextTrackDisplay` is ready. 12028 */ 12029 function TextTrackDisplay(player, options, ready) { 12030 classCallCheck(this, TextTrackDisplay); 12031 12032 var _this = possibleConstructorReturn(this, _Component.call(this, player, options, ready)); 12033 12034 player.on('loadstart', bind(_this, _this.toggleDisplay)); 12035 player.on('texttrackchange', bind(_this, _this.updateDisplay)); 12036 player.on('loadstart', bind(_this, _this.preselectTrack)); 12037 12038 // This used to be called during player init, but was causing an error 12039 // if a track should show by default and the display hadn't loaded yet. 12040 // Should probably be moved to an external track loader when we support 12041 // tracks that don't need a display. 12042 player.ready(bind(_this, function () { 12043 if (player.tech_ && player.tech_.featuresNativeTextTracks) { 12044 this.hide(); 12045 return; 12046 } 12047 12048 player.on('fullscreenchange', bind(this, this.updateDisplay)); 12049 12050 var tracks = this.options_.playerOptions.tracks || []; 12051 12052 for (var i = 0; i < tracks.length; i++) { 12053 this.player_.addRemoteTextTrack(tracks[i], true); 12054 } 12055 12056 this.preselectTrack(); 12057 })); 12058 return _this; 12059 } 12060 12061 /** 12062 * Preselect a track following this precedence: 12063 * - matches the previously selected {@link TextTrack}'s language and kind 12064 * - matches the previously selected {@link TextTrack}'s language only 12065 * - is the first default captions track 12066 * - is the first default descriptions track 12067 * 12068 * @listens Player#loadstart 12069 */ 12070 12071 12072 TextTrackDisplay.prototype.preselectTrack = function preselectTrack() { 12073 var modes = { captions: 1, subtitles: 1 }; 12074 var trackList = this.player_.textTracks(); 12075 var userPref = this.player_.cache_.selectedLanguage; 12076 var firstDesc = void 0; 12077 var firstCaptions = void 0; 12078 var preferredTrack = void 0; 12079 12080 for (var i = 0; i < trackList.length; i++) { 12081 var track = trackList[i]; 12082 12083 if (userPref && userPref.enabled && userPref.language === track.language) { 12084 // Always choose the track that matches both language and kind 12085 if (track.kind === userPref.kind) { 12086 preferredTrack = track; 12087 // or choose the first track that matches language 12088 } else if (!preferredTrack) { 12089 preferredTrack = track; 12090 } 12091 12092 // clear everything if offTextTrackMenuItem was clicked 12093 } else if (userPref && !userPref.enabled) { 12094 preferredTrack = null; 12095 firstDesc = null; 12096 firstCaptions = null; 12097 } else if (track['default']) { 12098 if (track.kind === 'descriptions' && !firstDesc) { 12099 firstDesc = track; 12100 } else if (track.kind in modes && !firstCaptions) { 12101 firstCaptions = track; 12102 } 12103 } 12104 } 12105 12106 // The preferredTrack matches the user preference and takes 12107 // precendence over all the other tracks. 12108 // So, display the preferredTrack before the first default track 12109 // and the subtitles/captions track before the descriptions track 12110 if (preferredTrack) { 12111 preferredTrack.mode = 'showing'; 12112 } else if (firstCaptions) { 12113 firstCaptions.mode = 'showing'; 12114 } else if (firstDesc) { 12115 firstDesc.mode = 'showing'; 12116 } 12117 }; 12118 12119 /** 12120 * Turn display of {@link TextTrack}'s from the current state into the other state. 12121 * There are only two states: 12122 * - 'shown' 12123 * - 'hidden' 12124 * 12125 * @listens Player#loadstart 12126 */ 12127 12128 12129 TextTrackDisplay.prototype.toggleDisplay = function toggleDisplay() { 12130 if (this.player_.tech_ && this.player_.tech_.featuresNativeTextTracks) { 12131 this.hide(); 12132 } else { 12133 this.show(); 12134 } 12135 }; 12136 12137 /** 12138 * Create the {@link Component}'s DOM element. 12139 * 12140 * @return {Element} 12141 * The element that was created. 12142 */ 12143 12144 12145 TextTrackDisplay.prototype.createEl = function createEl() { 12146 return _Component.prototype.createEl.call(this, 'div', { 12147 className: 'vjs-text-track-display' 12148 }, { 12149 'aria-live': 'off', 12150 'aria-atomic': 'true' 12151 }); 12152 }; 12153 12154 /** 12155 * Clear all displayed {@link TextTrack}s. 12156 */ 12157 12158 12159 TextTrackDisplay.prototype.clearDisplay = function clearDisplay() { 12160 if (typeof window_1.WebVTT === 'function') { 12161 window_1.WebVTT.processCues(window_1, [], this.el_); 12162 } 12163 }; 12164 12165 /** 12166 * Update the displayed TextTrack when a either a {@link Player#texttrackchange} or 12167 * a {@link Player#fullscreenchange} is fired. 12168 * 12169 * @listens Player#texttrackchange 12170 * @listens Player#fullscreenchange 12171 */ 12172 12173 12174 TextTrackDisplay.prototype.updateDisplay = function updateDisplay() { 12175 var tracks = this.player_.textTracks(); 12176 12177 this.clearDisplay(); 12178 12179 // Track display prioritization model: if multiple tracks are 'showing', 12180 // display the first 'subtitles' or 'captions' track which is 'showing', 12181 // otherwise display the first 'descriptions' track which is 'showing' 12182 12183 var descriptionsTrack = null; 12184 var captionsSubtitlesTrack = null; 12185 var i = tracks.length; 12186 12187 while (i--) { 12188 var track = tracks[i]; 12189 12190 if (track.mode === 'showing') { 12191 if (track.kind === 'descriptions') { 12192 descriptionsTrack = track; 12193 } else { 12194 captionsSubtitlesTrack = track; 12195 } 12196 } 12197 } 12198 12199 if (captionsSubtitlesTrack) { 12200 if (this.getAttribute('aria-live') !== 'off') { 12201 this.setAttribute('aria-live', 'off'); 12202 } 12203 this.updateForTrack(captionsSubtitlesTrack); 12204 } else if (descriptionsTrack) { 12205 if (this.getAttribute('aria-live') !== 'assertive') { 12206 this.setAttribute('aria-live', 'assertive'); 12207 } 12208 this.updateForTrack(descriptionsTrack); 12209 } 12210 }; 12211 12212 /** 12213 * Add an {@link Texttrack} to to the {@link Tech}s {@link TextTrackList}. 12214 * 12215 * @param {TextTrack} track 12216 * Text track object to be added to the list. 12217 */ 12218 12219 12220 TextTrackDisplay.prototype.updateForTrack = function updateForTrack(track) { 12221 if (typeof window_1.WebVTT !== 'function' || !track.activeCues) { 12222 return; 12223 } 12224 12225 var overrides = this.player_.textTrackSettings.getValues(); 12226 var cues = []; 12227 12228 for (var _i = 0; _i < track.activeCues.length; _i++) { 12229 cues.push(track.activeCues[_i]); 12230 } 12231 12232 window_1.WebVTT.processCues(window_1, cues, this.el_); 12233 12234 var i = cues.length; 12235 12236 while (i--) { 12237 var cue = cues[i]; 12238 12239 if (!cue) { 12240 continue; 12241 } 12242 12243 var cueDiv = cue.displayState; 12244 12245 if (overrides.color) { 12246 cueDiv.firstChild.style.color = overrides.color; 12247 } 12248 if (overrides.textOpacity) { 12249 tryUpdateStyle(cueDiv.firstChild, 'color', constructColor(overrides.color || '#fff', overrides.textOpacity)); 12250 } 12251 if (overrides.backgroundColor) { 12252 cueDiv.firstChild.style.backgroundColor = overrides.backgroundColor; 12253 } 12254 if (overrides.backgroundOpacity) { 12255 tryUpdateStyle(cueDiv.firstChild, 'backgroundColor', constructColor(overrides.backgroundColor || '#000', overrides.backgroundOpacity)); 12256 } 12257 if (overrides.windowColor) { 12258 if (overrides.windowOpacity) { 12259 tryUpdateStyle(cueDiv, 'backgroundColor', constructColor(overrides.windowColor, overrides.windowOpacity)); 12260 } else { 12261 cueDiv.style.backgroundColor = overrides.windowColor; 12262 } 12263 } 12264 if (overrides.edgeStyle) { 12265 if (overrides.edgeStyle === 'dropshadow') { 12266 cueDiv.firstChild.style.textShadow = '2px 2px 3px ' + darkGray + ', 2px 2px 4px ' + darkGray + ', 2px 2px 5px ' + darkGray; 12267 } else if (overrides.edgeStyle === 'raised') { 12268 cueDiv.firstChild.style.textShadow = '1px 1px ' + darkGray + ', 2px 2px ' + darkGray + ', 3px 3px ' + darkGray; 12269 } else if (overrides.edgeStyle === 'depressed') { 12270 cueDiv.firstChild.style.textShadow = '1px 1px ' + lightGray + ', 0 1px ' + lightGray + ', -1px -1px ' + darkGray + ', 0 -1px ' + darkGray; 12271 } else if (overrides.edgeStyle === 'uniform') { 12272 cueDiv.firstChild.style.textShadow = '0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray; 12273 } 12274 } 12275 if (overrides.fontPercent && overrides.fontPercent !== 1) { 12276 var fontSize = window_1.parseFloat(cueDiv.style.fontSize); 12277 12278 cueDiv.style.fontSize = fontSize * overrides.fontPercent + 'px'; 12279 cueDiv.style.height = 'auto'; 12280 cueDiv.style.top = 'auto'; 12281 cueDiv.style.bottom = '2px'; 12282 } 12283 if (overrides.fontFamily && overrides.fontFamily !== 'default') { 12284 if (overrides.fontFamily === 'small-caps') { 12285 cueDiv.firstChild.style.fontVariant = 'small-caps'; 12286 } else { 12287 cueDiv.firstChild.style.fontFamily = fontMap[overrides.fontFamily]; 12288 } 12289 } 12290 } 12291 }; 12292 12293 return TextTrackDisplay; 12294}(Component); 12295 12296Component.registerComponent('TextTrackDisplay', TextTrackDisplay); 12297 12298/** 12299 * @file loading-spinner.js 12300 */ 12301/** 12302 * A loading spinner for use during waiting/loading events. 12303 * 12304 * @extends Component 12305 */ 12306 12307var LoadingSpinner = function (_Component) { 12308 inherits(LoadingSpinner, _Component); 12309 12310 function LoadingSpinner() { 12311 classCallCheck(this, LoadingSpinner); 12312 return possibleConstructorReturn(this, _Component.apply(this, arguments)); 12313 } 12314 12315 /** 12316 * Create the `LoadingSpinner`s DOM element. 12317 * 12318 * @return {Element} 12319 * The dom element that gets created. 12320 */ 12321 LoadingSpinner.prototype.createEl = function createEl() { 12322 return _Component.prototype.createEl.call(this, 'div', { 12323 className: 'vjs-loading-spinner', 12324 dir: 'ltr' 12325 }); 12326 }; 12327 12328 return LoadingSpinner; 12329}(Component); 12330 12331Component.registerComponent('LoadingSpinner', LoadingSpinner); 12332 12333/** 12334 * @file button.js 12335 */ 12336/** 12337 * Base class for all buttons. 12338 * 12339 * @extends ClickableComponent 12340 */ 12341 12342var Button = function (_ClickableComponent) { 12343 inherits(Button, _ClickableComponent); 12344 12345 function Button() { 12346 classCallCheck(this, Button); 12347 return possibleConstructorReturn(this, _ClickableComponent.apply(this, arguments)); 12348 } 12349 12350 /** 12351 * Create the `Button`s DOM element. 12352 * 12353 * @param {string} [tag="button"] 12354 * The element's node type. This argument is IGNORED: no matter what 12355 * is passed, it will always create a `button` element. 12356 * 12357 * @param {Object} [props={}] 12358 * An object of properties that should be set on the element. 12359 * 12360 * @param {Object} [attributes={}] 12361 * An object of attributes that should be set on the element. 12362 * 12363 * @return {Element} 12364 * The element that gets created. 12365 */ 12366 Button.prototype.createEl = function createEl(tag) { 12367 var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 12368 var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; 12369 12370 tag = 'button'; 12371 12372 props = assign({ 12373 innerHTML: '<span aria-hidden="true" class="vjs-icon-placeholder"></span>', 12374 className: this.buildCSSClass() 12375 }, props); 12376 12377 // Add attributes for button element 12378 attributes = assign({ 12379 12380 // Necessary since the default button type is "submit" 12381 'type': 'button', 12382 12383 // let the screen reader user know that the text of the button may change 12384 'aria-live': 'polite' 12385 }, attributes); 12386 12387 var el = Component.prototype.createEl.call(this, tag, props, attributes); 12388 12389 this.createControlTextEl(el); 12390 12391 return el; 12392 }; 12393 12394 /** 12395 * Add a child `Component` inside of this `Button`. 12396 * 12397 * @param {string|Component} child 12398 * The name or instance of a child to add. 12399 * 12400 * @param {Object} [options={}] 12401 * The key/value store of options that will get passed to children of 12402 * the child. 12403 * 12404 * @return {Component} 12405 * The `Component` that gets added as a child. When using a string the 12406 * `Component` will get created by this process. 12407 * 12408 * @deprecated since version 5 12409 */ 12410 12411 12412 Button.prototype.addChild = function addChild(child) { 12413 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 12414 12415 var className = this.constructor.name; 12416 12417 log$1.warn('Adding an actionable (user controllable) child to a Button (' + className + ') is not supported; use a ClickableComponent instead.'); 12418 12419 // Avoid the error message generated by ClickableComponent's addChild method 12420 return Component.prototype.addChild.call(this, child, options); 12421 }; 12422 12423 /** 12424 * Enable the `Button` element so that it can be activated or clicked. Use this with 12425 * {@link Button#disable}. 12426 */ 12427 12428 12429 Button.prototype.enable = function enable() { 12430 _ClickableComponent.prototype.enable.call(this); 12431 this.el_.removeAttribute('disabled'); 12432 }; 12433 12434 /** 12435 * Disable the `Button` element so that it cannot be activated or clicked. Use this with 12436 * {@link Button#enable}. 12437 */ 12438 12439 12440 Button.prototype.disable = function disable() { 12441 _ClickableComponent.prototype.disable.call(this); 12442 this.el_.setAttribute('disabled', 'disabled'); 12443 }; 12444 12445 /** 12446 * This gets called when a `Button` has focus and `keydown` is triggered via a key 12447 * press. 12448 * 12449 * @param {EventTarget~Event} event 12450 * The event that caused this function to get called. 12451 * 12452 * @listens keydown 12453 */ 12454 12455 12456 Button.prototype.handleKeyPress = function handleKeyPress(event) { 12457 12458 // Ignore Space (32) or Enter (13) key operation, which is handled by the browser for a button. 12459 if (event.which === 32 || event.which === 13) { 12460 return; 12461 } 12462 12463 // Pass keypress handling up for unsupported keys 12464 _ClickableComponent.prototype.handleKeyPress.call(this, event); 12465 }; 12466 12467 return Button; 12468}(ClickableComponent); 12469 12470Component.registerComponent('Button', Button); 12471 12472/** 12473 * @file big-play-button.js 12474 */ 12475/** 12476 * The initial play button that shows before the video has played. The hiding of the 12477 * `BigPlayButton` get done via CSS and `Player` states. 12478 * 12479 * @extends Button 12480 */ 12481 12482var BigPlayButton = function (_Button) { 12483 inherits(BigPlayButton, _Button); 12484 12485 function BigPlayButton(player, options) { 12486 classCallCheck(this, BigPlayButton); 12487 12488 var _this = possibleConstructorReturn(this, _Button.call(this, player, options)); 12489 12490 _this.mouseused_ = false; 12491 12492 _this.on('mousedown', _this.handleMouseDown); 12493 return _this; 12494 } 12495 12496 /** 12497 * Builds the default DOM `className`. 12498 * 12499 * @return {string} 12500 * The DOM `className` for this object. Always returns 'vjs-big-play-button'. 12501 */ 12502 12503 12504 BigPlayButton.prototype.buildCSSClass = function buildCSSClass() { 12505 return 'vjs-big-play-button'; 12506 }; 12507 12508 /** 12509 * This gets called when a `BigPlayButton` "clicked". See {@link ClickableComponent} 12510 * for more detailed information on what a click can be. 12511 * 12512 * @param {EventTarget~Event} event 12513 * The `keydown`, `tap`, or `click` event that caused this function to be 12514 * called. 12515 * 12516 * @listens tap 12517 * @listens click 12518 */ 12519 12520 12521 BigPlayButton.prototype.handleClick = function handleClick(event) { 12522 var playPromise = this.player_.play(); 12523 12524 // exit early if clicked via the mouse 12525 if (this.mouseused_ && event.clientX && event.clientY) { 12526 return; 12527 } 12528 12529 var cb = this.player_.getChild('controlBar'); 12530 var playToggle = cb && cb.getChild('playToggle'); 12531 12532 if (!playToggle) { 12533 this.player_.focus(); 12534 return; 12535 } 12536 12537 var playFocus = function playFocus() { 12538 return playToggle.focus(); 12539 }; 12540 12541 if (isPromise(playPromise)) { 12542 playPromise.then(playFocus, function () {}); 12543 } else { 12544 this.setTimeout(playFocus, 1); 12545 } 12546 }; 12547 12548 BigPlayButton.prototype.handleKeyPress = function handleKeyPress(event) { 12549 this.mouseused_ = false; 12550 12551 _Button.prototype.handleKeyPress.call(this, event); 12552 }; 12553 12554 BigPlayButton.prototype.handleMouseDown = function handleMouseDown(event) { 12555 this.mouseused_ = true; 12556 }; 12557 12558 return BigPlayButton; 12559}(Button); 12560 12561/** 12562 * The text that should display over the `BigPlayButton`s controls. Added to for localization. 12563 * 12564 * @type {string} 12565 * @private 12566 */ 12567 12568 12569BigPlayButton.prototype.controlText_ = 'Play Video'; 12570 12571Component.registerComponent('BigPlayButton', BigPlayButton); 12572 12573/** 12574 * @file close-button.js 12575 */ 12576/** 12577 * The `CloseButton` is a `{@link Button}` that fires a `close` event when 12578 * it gets clicked. 12579 * 12580 * @extends Button 12581 */ 12582 12583var CloseButton = function (_Button) { 12584 inherits(CloseButton, _Button); 12585 12586 /** 12587 * Creates an instance of the this class. 12588 * 12589 * @param {Player} player 12590 * The `Player` that this class should be attached to. 12591 * 12592 * @param {Object} [options] 12593 * The key/value store of player options. 12594 */ 12595 function CloseButton(player, options) { 12596 classCallCheck(this, CloseButton); 12597 12598 var _this = possibleConstructorReturn(this, _Button.call(this, player, options)); 12599 12600 _this.controlText(options && options.controlText || _this.localize('Close')); 12601 return _this; 12602 } 12603 12604 /** 12605 * Builds the default DOM `className`. 12606 * 12607 * @return {string} 12608 * The DOM `className` for this object. 12609 */ 12610 12611 12612 CloseButton.prototype.buildCSSClass = function buildCSSClass() { 12613 return 'vjs-close-button ' + _Button.prototype.buildCSSClass.call(this); 12614 }; 12615 12616 /** 12617 * This gets called when a `CloseButton` gets clicked. See 12618 * {@link ClickableComponent#handleClick} for more information on when this will be 12619 * triggered 12620 * 12621 * @param {EventTarget~Event} event 12622 * The `keydown`, `tap`, or `click` event that caused this function to be 12623 * called. 12624 * 12625 * @listens tap 12626 * @listens click 12627 * @fires CloseButton#close 12628 */ 12629 12630 12631 CloseButton.prototype.handleClick = function handleClick(event) { 12632 12633 /** 12634 * Triggered when the a `CloseButton` is clicked. 12635 * 12636 * @event CloseButton#close 12637 * @type {EventTarget~Event} 12638 * 12639 * @property {boolean} [bubbles=false] 12640 * set to false so that the close event does not 12641 * bubble up to parents if there is no listener 12642 */ 12643 this.trigger({ type: 'close', bubbles: false }); 12644 }; 12645 12646 return CloseButton; 12647}(Button); 12648 12649Component.registerComponent('CloseButton', CloseButton); 12650 12651/** 12652 * @file play-toggle.js 12653 */ 12654/** 12655 * Button to toggle between play and pause. 12656 * 12657 * @extends Button 12658 */ 12659 12660var PlayToggle = function (_Button) { 12661 inherits(PlayToggle, _Button); 12662 12663 /** 12664 * Creates an instance of this class. 12665 * 12666 * @param {Player} player 12667 * The `Player` that this class should be attached to. 12668 * 12669 * @param {Object} [options] 12670 * The key/value store of player options. 12671 */ 12672 function PlayToggle(player, options) { 12673 classCallCheck(this, PlayToggle); 12674 12675 var _this = possibleConstructorReturn(this, _Button.call(this, player, options)); 12676 12677 _this.on(player, 'play', _this.handlePlay); 12678 _this.on(player, 'pause', _this.handlePause); 12679 _this.on(player, 'ended', _this.handleEnded); 12680 return _this; 12681 } 12682 12683 /** 12684 * Builds the default DOM `className`. 12685 * 12686 * @return {string} 12687 * The DOM `className` for this object. 12688 */ 12689 12690 12691 PlayToggle.prototype.buildCSSClass = function buildCSSClass() { 12692 return 'vjs-play-control ' + _Button.prototype.buildCSSClass.call(this); 12693 }; 12694 12695 /** 12696 * This gets called when an `PlayToggle` is "clicked". See 12697 * {@link ClickableComponent} for more detailed information on what a click can be. 12698 * 12699 * @param {EventTarget~Event} [event] 12700 * The `keydown`, `tap`, or `click` event that caused this function to be 12701 * called. 12702 * 12703 * @listens tap 12704 * @listens click 12705 */ 12706 12707 12708 PlayToggle.prototype.handleClick = function handleClick(event) { 12709 if (this.player_.paused()) { 12710 this.player_.play(); 12711 } else { 12712 this.player_.pause(); 12713 } 12714 }; 12715 12716 /** 12717 * This gets called once after the video has ended and the user seeks so that 12718 * we can change the replay button back to a play button. 12719 * 12720 * @param {EventTarget~Event} [event] 12721 * The event that caused this function to run. 12722 * 12723 * @listens Player#seeked 12724 */ 12725 12726 12727 PlayToggle.prototype.handleSeeked = function handleSeeked(event) { 12728 this.removeClass('vjs-ended'); 12729 12730 if (this.player_.paused()) { 12731 this.handlePause(event); 12732 } else { 12733 this.handlePlay(event); 12734 } 12735 }; 12736 12737 /** 12738 * Add the vjs-playing class to the element so it can change appearance. 12739 * 12740 * @param {EventTarget~Event} [event] 12741 * The event that caused this function to run. 12742 * 12743 * @listens Player#play 12744 */ 12745 12746 12747 PlayToggle.prototype.handlePlay = function handlePlay(event) { 12748 this.removeClass('vjs-ended'); 12749 this.removeClass('vjs-paused'); 12750 this.addClass('vjs-playing'); 12751 // change the button text to "Pause" 12752 this.controlText('Pause'); 12753 }; 12754 12755 /** 12756 * Add the vjs-paused class to the element so it can change appearance. 12757 * 12758 * @param {EventTarget~Event} [event] 12759 * The event that caused this function to run. 12760 * 12761 * @listens Player#pause 12762 */ 12763 12764 12765 PlayToggle.prototype.handlePause = function handlePause(event) { 12766 this.removeClass('vjs-playing'); 12767 this.addClass('vjs-paused'); 12768 // change the button text to "Play" 12769 this.controlText('Play'); 12770 }; 12771 12772 /** 12773 * Add the vjs-ended class to the element so it can change appearance 12774 * 12775 * @param {EventTarget~Event} [event] 12776 * The event that caused this function to run. 12777 * 12778 * @listens Player#ended 12779 */ 12780 12781 12782 PlayToggle.prototype.handleEnded = function handleEnded(event) { 12783 this.removeClass('vjs-playing'); 12784 this.addClass('vjs-ended'); 12785 // change the button text to "Replay" 12786 this.controlText('Replay'); 12787 12788 // on the next seek remove the replay button 12789 this.one(this.player_, 'seeked', this.handleSeeked); 12790 }; 12791 12792 return PlayToggle; 12793}(Button); 12794 12795/** 12796 * The text that should display over the `PlayToggle`s controls. Added for localization. 12797 * 12798 * @type {string} 12799 * @private 12800 */ 12801 12802 12803PlayToggle.prototype.controlText_ = 'Play'; 12804 12805Component.registerComponent('PlayToggle', PlayToggle); 12806 12807/** 12808 * @file format-time.js 12809 * @module Format-time 12810 */ 12811 12812/** 12813 * Format seconds as a time string, H:MM:SS or M:SS. Supplying a guide (in seconds) 12814 * will force a number of leading zeros to cover the length of the guide. 12815 * 12816 * @param {number} seconds 12817 * Number of seconds to be turned into a string 12818 * 12819 * @param {number} guide 12820 * Number (in seconds) to model the string after 12821 * 12822 * @return {string} 12823 * Time formatted as H:MM:SS or M:SS 12824 */ 12825function formatTime(seconds) { 12826 var guide = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : seconds; 12827 12828 seconds = seconds < 0 ? 0 : seconds; 12829 var s = Math.floor(seconds % 60); 12830 var m = Math.floor(seconds / 60 % 60); 12831 var h = Math.floor(seconds / 3600); 12832 var gm = Math.floor(guide / 60 % 60); 12833 var gh = Math.floor(guide / 3600); 12834 12835 // handle invalid times 12836 if (isNaN(seconds) || seconds === Infinity) { 12837 // '-' is false for all relational operators (e.g. <, >=) so this setting 12838 // will add the minimum number of fields specified by the guide 12839 h = m = s = '-'; 12840 } 12841 12842 // Check if we need to show hours 12843 h = h > 0 || gh > 0 ? h + ':' : ''; 12844 12845 // If hours are showing, we may need to add a leading zero. 12846 // Always show at least one digit of minutes. 12847 m = ((h || gm >= 10) && m < 10 ? '0' + m : m) + ':'; 12848 12849 // Check if leading zero is need for seconds 12850 s = s < 10 ? '0' + s : s; 12851 12852 return h + m + s; 12853} 12854 12855/** 12856 * @file time-display.js 12857 */ 12858/** 12859 * Displays the time left in the video 12860 * 12861 * @extends Component 12862 */ 12863 12864var TimeDisplay = function (_Component) { 12865 inherits(TimeDisplay, _Component); 12866 12867 /** 12868 * Creates an instance of this class. 12869 * 12870 * @param {Player} player 12871 * The `Player` that this class should be attached to. 12872 * 12873 * @param {Object} [options] 12874 * The key/value store of player options. 12875 */ 12876 function TimeDisplay(player, options) { 12877 classCallCheck(this, TimeDisplay); 12878 12879 var _this = possibleConstructorReturn(this, _Component.call(this, player, options)); 12880 12881 _this.throttledUpdateContent = throttle(bind(_this, _this.updateContent), 25); 12882 _this.on(player, 'timeupdate', _this.throttledUpdateContent); 12883 return _this; 12884 } 12885 12886 /** 12887 * Create the `Component`'s DOM element 12888 * 12889 * @return {Element} 12890 * The element that was created. 12891 */ 12892 12893 12894 TimeDisplay.prototype.createEl = function createEl$$1(plainName) { 12895 var className = this.buildCSSClass(); 12896 var el = _Component.prototype.createEl.call(this, 'div', { 12897 className: className + ' vjs-time-control vjs-control' 12898 }); 12899 12900 this.contentEl_ = createEl('div', { 12901 className: className + '-display' 12902 }, { 12903 // tell screen readers not to automatically read the time as it changes 12904 'aria-live': 'off' 12905 }, createEl('span', { 12906 className: 'vjs-control-text', 12907 textContent: this.localize(this.controlText_) 12908 })); 12909 12910 this.updateTextNode_(); 12911 el.appendChild(this.contentEl_); 12912 return el; 12913 }; 12914 12915 TimeDisplay.prototype.dispose = function dispose() { 12916 this.contentEl_ = null; 12917 this.textNode_ = null; 12918 12919 _Component.prototype.dispose.call(this); 12920 }; 12921 12922 /** 12923 * Updates the "remaining time" text node with new content using the 12924 * contents of the `formattedTime_` property. 12925 * 12926 * @private 12927 */ 12928 12929 12930 TimeDisplay.prototype.updateTextNode_ = function updateTextNode_() { 12931 if (!this.contentEl_) { 12932 return; 12933 } 12934 12935 while (this.contentEl_.firstChild) { 12936 this.contentEl_.removeChild(this.contentEl_.firstChild); 12937 } 12938 12939 this.textNode_ = document_1.createTextNode(this.formattedTime_ || '0:00'); 12940 this.contentEl_.appendChild(this.textNode_); 12941 }; 12942 12943 /** 12944 * Generates a formatted time for this component to use in display. 12945 * 12946 * @param {number} time 12947 * A numeric time, in seconds. 12948 * 12949 * @return {string} 12950 * A formatted time 12951 * 12952 * @private 12953 */ 12954 12955 12956 TimeDisplay.prototype.formatTime_ = function formatTime_(time) { 12957 return formatTime(time); 12958 }; 12959 12960 /** 12961 * Updates the time display text node if it has what was passed in changed 12962 * the formatted time. 12963 * 12964 * @param {number} time 12965 * The time to update to 12966 * 12967 * @private 12968 */ 12969 12970 12971 TimeDisplay.prototype.updateFormattedTime_ = function updateFormattedTime_(time) { 12972 var formattedTime = this.formatTime_(time); 12973 12974 if (formattedTime === this.formattedTime_) { 12975 return; 12976 } 12977 12978 this.formattedTime_ = formattedTime; 12979 this.requestAnimationFrame(this.updateTextNode_); 12980 }; 12981 12982 /** 12983 * To be filled out in the child class, should update the displayed time 12984 * in accordance with the fact that the current time has changed. 12985 * 12986 * @param {EventTarget~Event} [event] 12987 * The `timeupdate` event that caused this to run. 12988 * 12989 * @listens Player#timeupdate 12990 */ 12991 12992 12993 TimeDisplay.prototype.updateContent = function updateContent(event) {}; 12994 12995 return TimeDisplay; 12996}(Component); 12997 12998/** 12999 * The text that should display over the `TimeDisplay`s controls. Added to for localization. 13000 * 13001 * @type {string} 13002 * @private 13003 */ 13004 13005 13006TimeDisplay.prototype.controlText_ = 'Time'; 13007 13008Component.registerComponent('TimeDisplay', TimeDisplay); 13009 13010/** 13011 * @file current-time-display.js 13012 */ 13013/** 13014 * Displays the current time 13015 * 13016 * @extends Component 13017 */ 13018 13019var CurrentTimeDisplay = function (_TimeDisplay) { 13020 inherits(CurrentTimeDisplay, _TimeDisplay); 13021 13022 /** 13023 * Creates an instance of this class. 13024 * 13025 * @param {Player} player 13026 * The `Player` that this class should be attached to. 13027 * 13028 * @param {Object} [options] 13029 * The key/value store of player options. 13030 */ 13031 function CurrentTimeDisplay(player, options) { 13032 classCallCheck(this, CurrentTimeDisplay); 13033 13034 var _this = possibleConstructorReturn(this, _TimeDisplay.call(this, player, options)); 13035 13036 _this.on(player, 'ended', _this.handleEnded); 13037 return _this; 13038 } 13039 13040 /** 13041 * Builds the default DOM `className`. 13042 * 13043 * @return {string} 13044 * The DOM `className` for this object. 13045 */ 13046 13047 13048 CurrentTimeDisplay.prototype.buildCSSClass = function buildCSSClass() { 13049 return 'vjs-current-time'; 13050 }; 13051 13052 /** 13053 * Update current time display 13054 * 13055 * @param {EventTarget~Event} [event] 13056 * The `timeupdate` event that caused this function to run. 13057 * 13058 * @listens Player#timeupdate 13059 */ 13060 13061 13062 CurrentTimeDisplay.prototype.updateContent = function updateContent(event) { 13063 // Allows for smooth scrubbing, when player can't keep up. 13064 var time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime(); 13065 13066 this.updateFormattedTime_(time); 13067 }; 13068 13069 /** 13070 * When the player fires ended there should be no time left. Sadly 13071 * this is not always the case, lets make it seem like that is the case 13072 * for users. 13073 * 13074 * @param {EventTarget~Event} [event] 13075 * The `ended` event that caused this to run. 13076 * 13077 * @listens Player#ended 13078 */ 13079 13080 13081 CurrentTimeDisplay.prototype.handleEnded = function handleEnded(event) { 13082 if (!this.player_.duration()) { 13083 return; 13084 } 13085 this.updateFormattedTime_(this.player_.duration()); 13086 }; 13087 13088 return CurrentTimeDisplay; 13089}(TimeDisplay); 13090 13091/** 13092 * The text that should display over the `CurrentTimeDisplay`s controls. Added to for localization. 13093 * 13094 * @type {string} 13095 * @private 13096 */ 13097 13098 13099CurrentTimeDisplay.prototype.controlText_ = 'Current Time'; 13100 13101Component.registerComponent('CurrentTimeDisplay', CurrentTimeDisplay); 13102 13103/** 13104 * @file duration-display.js 13105 */ 13106/** 13107 * Displays the duration 13108 * 13109 * @extends Component 13110 */ 13111 13112var DurationDisplay = function (_TimeDisplay) { 13113 inherits(DurationDisplay, _TimeDisplay); 13114 13115 /** 13116 * Creates an instance of this class. 13117 * 13118 * @param {Player} player 13119 * The `Player` that this class should be attached to. 13120 * 13121 * @param {Object} [options] 13122 * The key/value store of player options. 13123 */ 13124 function DurationDisplay(player, options) { 13125 classCallCheck(this, DurationDisplay); 13126 13127 // we do not want to/need to throttle duration changes, 13128 // as they should always display the changed duration as 13129 // it has changed 13130 var _this = possibleConstructorReturn(this, _TimeDisplay.call(this, player, options)); 13131 13132 _this.on(player, 'durationchange', _this.updateContent); 13133 13134 // Also listen for timeupdate (in the parent) and loadedmetadata because removing those 13135 // listeners could have broken dependent applications/libraries. These 13136 // can likely be removed for 7.0. 13137 _this.on(player, 'loadedmetadata', _this.throttledUpdateContent); 13138 return _this; 13139 } 13140 13141 /** 13142 * Builds the default DOM `className`. 13143 * 13144 * @return {string} 13145 * The DOM `className` for this object. 13146 */ 13147 13148 13149 DurationDisplay.prototype.buildCSSClass = function buildCSSClass() { 13150 return 'vjs-duration'; 13151 }; 13152 13153 /** 13154 * Update duration time display. 13155 * 13156 * @param {EventTarget~Event} [event] 13157 * The `durationchange`, `timeupdate`, or `loadedmetadata` event that caused 13158 * this function to be called. 13159 * 13160 * @listens Player#durationchange 13161 * @listens Player#timeupdate 13162 * @listens Player#loadedmetadata 13163 */ 13164 13165 13166 DurationDisplay.prototype.updateContent = function updateContent(event) { 13167 var duration = this.player_.duration(); 13168 13169 if (duration && this.duration_ !== duration) { 13170 this.duration_ = duration; 13171 this.updateFormattedTime_(duration); 13172 } 13173 }; 13174 13175 return DurationDisplay; 13176}(TimeDisplay); 13177 13178/** 13179 * The text that should display over the `DurationDisplay`s controls. Added to for localization. 13180 * 13181 * @type {string} 13182 * @private 13183 */ 13184 13185 13186DurationDisplay.prototype.controlText_ = 'Duration Time'; 13187 13188Component.registerComponent('DurationDisplay', DurationDisplay); 13189 13190/** 13191 * @file time-divider.js 13192 */ 13193/** 13194 * The separator between the current time and duration. 13195 * Can be hidden if it's not needed in the design. 13196 * 13197 * @extends Component 13198 */ 13199 13200var TimeDivider = function (_Component) { 13201 inherits(TimeDivider, _Component); 13202 13203 function TimeDivider() { 13204 classCallCheck(this, TimeDivider); 13205 return possibleConstructorReturn(this, _Component.apply(this, arguments)); 13206 } 13207 13208 /** 13209 * Create the component's DOM element 13210 * 13211 * @return {Element} 13212 * The element that was created. 13213 */ 13214 TimeDivider.prototype.createEl = function createEl() { 13215 return _Component.prototype.createEl.call(this, 'div', { 13216 className: 'vjs-time-control vjs-time-divider', 13217 innerHTML: '<div><span>/</span></div>' 13218 }); 13219 }; 13220 13221 return TimeDivider; 13222}(Component); 13223 13224Component.registerComponent('TimeDivider', TimeDivider); 13225 13226/** 13227 * @file remaining-time-display.js 13228 */ 13229/** 13230 * Displays the time left in the video 13231 * 13232 * @extends Component 13233 */ 13234 13235var RemainingTimeDisplay = function (_TimeDisplay) { 13236 inherits(RemainingTimeDisplay, _TimeDisplay); 13237 13238 /** 13239 * Creates an instance of this class. 13240 * 13241 * @param {Player} player 13242 * The `Player` that this class should be attached to. 13243 * 13244 * @param {Object} [options] 13245 * The key/value store of player options. 13246 */ 13247 function RemainingTimeDisplay(player, options) { 13248 classCallCheck(this, RemainingTimeDisplay); 13249 13250 var _this = possibleConstructorReturn(this, _TimeDisplay.call(this, player, options)); 13251 13252 _this.on(player, 'durationchange', _this.throttledUpdateContent); 13253 _this.on(player, 'ended', _this.handleEnded); 13254 return _this; 13255 } 13256 13257 /** 13258 * Builds the default DOM `className`. 13259 * 13260 * @return {string} 13261 * The DOM `className` for this object. 13262 */ 13263 13264 13265 RemainingTimeDisplay.prototype.buildCSSClass = function buildCSSClass() { 13266 return 'vjs-remaining-time'; 13267 }; 13268 13269 /** 13270 * The remaining time display prefixes numbers with a "minus" character. 13271 * 13272 * @param {number} time 13273 * A numeric time, in seconds. 13274 * 13275 * @return {string} 13276 * A formatted time 13277 * 13278 * @private 13279 */ 13280 13281 13282 RemainingTimeDisplay.prototype.formatTime_ = function formatTime_(time) { 13283 return '-' + _TimeDisplay.prototype.formatTime_.call(this, time); 13284 }; 13285 13286 /** 13287 * Update remaining time display. 13288 * 13289 * @param {EventTarget~Event} [event] 13290 * The `timeupdate` or `durationchange` event that caused this to run. 13291 * 13292 * @listens Player#timeupdate 13293 * @listens Player#durationchange 13294 */ 13295 13296 13297 RemainingTimeDisplay.prototype.updateContent = function updateContent(event) { 13298 if (!this.player_.duration()) { 13299 return; 13300 } 13301 13302 // @deprecated We should only use remainingTimeDisplay 13303 // as of video.js 7 13304 if (this.player_.remainingTimeDisplay) { 13305 this.updateFormattedTime_(this.player_.remainingTimeDisplay()); 13306 } else { 13307 this.updateFormattedTime_(this.player_.remainingTime()); 13308 } 13309 }; 13310 13311 /** 13312 * When the player fires ended there should be no time left. Sadly 13313 * this is not always the case, lets make it seem like that is the case 13314 * for users. 13315 * 13316 * @param {EventTarget~Event} [event] 13317 * The `ended` event that caused this to run. 13318 * 13319 * @listens Player#ended 13320 */ 13321 13322 13323 RemainingTimeDisplay.prototype.handleEnded = function handleEnded(event) { 13324 if (!this.player_.duration()) { 13325 return; 13326 } 13327 this.updateFormattedTime_(0); 13328 }; 13329 13330 return RemainingTimeDisplay; 13331}(TimeDisplay); 13332 13333/** 13334 * The text that should display over the `RemainingTimeDisplay`s controls. Added to for localization. 13335 * 13336 * @type {string} 13337 * @private 13338 */ 13339 13340 13341RemainingTimeDisplay.prototype.controlText_ = 'Remaining Time'; 13342 13343Component.registerComponent('RemainingTimeDisplay', RemainingTimeDisplay); 13344 13345/** 13346 * @file live-display.js 13347 */ 13348// TODO - Future make it click to snap to live 13349 13350/** 13351 * Displays the live indicator when duration is Infinity. 13352 * 13353 * @extends Component 13354 */ 13355 13356var LiveDisplay = function (_Component) { 13357 inherits(LiveDisplay, _Component); 13358 13359 /** 13360 * Creates an instance of this class. 13361 * 13362 * @param {Player} player 13363 * The `Player` that this class should be attached to. 13364 * 13365 * @param {Object} [options] 13366 * The key/value store of player options. 13367 */ 13368 function LiveDisplay(player, options) { 13369 classCallCheck(this, LiveDisplay); 13370 13371 var _this = possibleConstructorReturn(this, _Component.call(this, player, options)); 13372 13373 _this.updateShowing(); 13374 _this.on(_this.player(), 'durationchange', _this.updateShowing); 13375 return _this; 13376 } 13377 13378 /** 13379 * Create the `Component`'s DOM element 13380 * 13381 * @return {Element} 13382 * The element that was created. 13383 */ 13384 13385 13386 LiveDisplay.prototype.createEl = function createEl$$1() { 13387 var el = _Component.prototype.createEl.call(this, 'div', { 13388 className: 'vjs-live-control vjs-control' 13389 }); 13390 13391 this.contentEl_ = createEl('div', { 13392 className: 'vjs-live-display', 13393 innerHTML: '<span class="vjs-control-text">' + this.localize('Stream Type') + '</span>' + this.localize('LIVE') 13394 }, { 13395 'aria-live': 'off' 13396 }); 13397 13398 el.appendChild(this.contentEl_); 13399 return el; 13400 }; 13401 13402 LiveDisplay.prototype.dispose = function dispose() { 13403 this.contentEl_ = null; 13404 13405 _Component.prototype.dispose.call(this); 13406 }; 13407 13408 /** 13409 * Check the duration to see if the LiveDisplay should be showing or not. Then show/hide 13410 * it accordingly 13411 * 13412 * @param {EventTarget~Event} [event] 13413 * The {@link Player#durationchange} event that caused this function to run. 13414 * 13415 * @listens Player#durationchange 13416 */ 13417 13418 13419 LiveDisplay.prototype.updateShowing = function updateShowing(event) { 13420 if (this.player().duration() === Infinity) { 13421 this.show(); 13422 } else { 13423 this.hide(); 13424 } 13425 }; 13426 13427 return LiveDisplay; 13428}(Component); 13429 13430Component.registerComponent('LiveDisplay', LiveDisplay); 13431 13432/** 13433 * @file slider.js 13434 */ 13435/** 13436 * The base functionality for a slider. Can be vertical or horizontal. 13437 * For instance the volume bar or the seek bar on a video is a slider. 13438 * 13439 * @extends Component 13440 */ 13441 13442var Slider = function (_Component) { 13443 inherits(Slider, _Component); 13444 13445 /** 13446 * Create an instance of this class 13447 * 13448 * @param {Player} player 13449 * The `Player` that this class should be attached to. 13450 * 13451 * @param {Object} [options] 13452 * The key/value store of player options. 13453 */ 13454 function Slider(player, options) { 13455 classCallCheck(this, Slider); 13456 13457 // Set property names to bar to match with the child Slider class is looking for 13458 var _this = possibleConstructorReturn(this, _Component.call(this, player, options)); 13459 13460 _this.bar = _this.getChild(_this.options_.barName); 13461 13462 // Set a horizontal or vertical class on the slider depending on the slider type 13463 _this.vertical(!!_this.options_.vertical); 13464 13465 _this.enable(); 13466 return _this; 13467 } 13468 13469 /** 13470 * Are controls are currently enabled for this slider or not. 13471 * 13472 * @return {boolean} 13473 * true if controls are enabled, false otherwise 13474 */ 13475 13476 13477 Slider.prototype.enabled = function enabled() { 13478 return this.enabled_; 13479 }; 13480 13481 /** 13482 * Enable controls for this slider if they are disabled 13483 */ 13484 13485 13486 Slider.prototype.enable = function enable() { 13487 if (this.enabled()) { 13488 return; 13489 } 13490 13491 this.on('mousedown', this.handleMouseDown); 13492 this.on('touchstart', this.handleMouseDown); 13493 this.on('focus', this.handleFocus); 13494 this.on('blur', this.handleBlur); 13495 this.on('click', this.handleClick); 13496 13497 this.on(this.player_, 'controlsvisible', this.update); 13498 13499 if (this.playerEvent) { 13500 this.on(this.player_, this.playerEvent, this.update); 13501 } 13502 13503 this.removeClass('disabled'); 13504 this.setAttribute('tabindex', 0); 13505 13506 this.enabled_ = true; 13507 }; 13508 13509 /** 13510 * Disable controls for this slider if they are enabled 13511 */ 13512 13513 13514 Slider.prototype.disable = function disable() { 13515 if (!this.enabled()) { 13516 return; 13517 } 13518 var doc = this.bar.el_.ownerDocument; 13519 13520 this.off('mousedown', this.handleMouseDown); 13521 this.off('touchstart', this.handleMouseDown); 13522 this.off('focus', this.handleFocus); 13523 this.off('blur', this.handleBlur); 13524 this.off('click', this.handleClick); 13525 this.off(this.player_, 'controlsvisible', this.update); 13526 this.off(doc, 'mousemove', this.handleMouseMove); 13527 this.off(doc, 'mouseup', this.handleMouseUp); 13528 this.off(doc, 'touchmove', this.handleMouseMove); 13529 this.off(doc, 'touchend', this.handleMouseUp); 13530 this.removeAttribute('tabindex'); 13531 13532 this.addClass('disabled'); 13533 13534 if (this.playerEvent) { 13535 this.off(this.player_, this.playerEvent, this.update); 13536 } 13537 this.enabled_ = false; 13538 }; 13539 13540 /** 13541 * Create the `Button`s DOM element. 13542 * 13543 * @param {string} type 13544 * Type of element to create. 13545 * 13546 * @param {Object} [props={}] 13547 * List of properties in Object form. 13548 * 13549 * @param {Object} [attributes={}] 13550 * list of attributes in Object form. 13551 * 13552 * @return {Element} 13553 * The element that gets created. 13554 */ 13555 13556 13557 Slider.prototype.createEl = function createEl$$1(type) { 13558 var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 13559 var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; 13560 13561 // Add the slider element class to all sub classes 13562 props.className = props.className + ' vjs-slider'; 13563 props = assign({ 13564 tabIndex: 0 13565 }, props); 13566 13567 attributes = assign({ 13568 'role': 'slider', 13569 'aria-valuenow': 0, 13570 'aria-valuemin': 0, 13571 'aria-valuemax': 100, 13572 'tabIndex': 0 13573 }, attributes); 13574 13575 return _Component.prototype.createEl.call(this, type, props, attributes); 13576 }; 13577 13578 /** 13579 * Handle `mousedown` or `touchstart` events on the `Slider`. 13580 * 13581 * @param {EventTarget~Event} event 13582 * `mousedown` or `touchstart` event that triggered this function 13583 * 13584 * @listens mousedown 13585 * @listens touchstart 13586 * @fires Slider#slideractive 13587 */ 13588 13589 13590 Slider.prototype.handleMouseDown = function handleMouseDown(event) { 13591 var doc = this.bar.el_.ownerDocument; 13592 13593 event.preventDefault(); 13594 blockTextSelection(); 13595 13596 this.addClass('vjs-sliding'); 13597 /** 13598 * Triggered when the slider is in an active state 13599 * 13600 * @event Slider#slideractive 13601 * @type {EventTarget~Event} 13602 */ 13603 this.trigger('slideractive'); 13604 13605 this.on(doc, 'mousemove', this.handleMouseMove); 13606 this.on(doc, 'mouseup', this.handleMouseUp); 13607 this.on(doc, 'touchmove', this.handleMouseMove); 13608 this.on(doc, 'touchend', this.handleMouseUp); 13609 13610 this.handleMouseMove(event); 13611 }; 13612 13613 /** 13614 * Handle the `mousemove`, `touchmove`, and `mousedown` events on this `Slider`. 13615 * The `mousemove` and `touchmove` events will only only trigger this function during 13616 * `mousedown` and `touchstart`. This is due to {@link Slider#handleMouseDown} and 13617 * {@link Slider#handleMouseUp}. 13618 * 13619 * @param {EventTarget~Event} event 13620 * `mousedown`, `mousemove`, `touchstart`, or `touchmove` event that triggered 13621 * this function 13622 * 13623 * @listens mousemove 13624 * @listens touchmove 13625 */ 13626 13627 13628 Slider.prototype.handleMouseMove = function handleMouseMove(event) {}; 13629 13630 /** 13631 * Handle `mouseup` or `touchend` events on the `Slider`. 13632 * 13633 * @param {EventTarget~Event} event 13634 * `mouseup` or `touchend` event that triggered this function. 13635 * 13636 * @listens touchend 13637 * @listens mouseup 13638 * @fires Slider#sliderinactive 13639 */ 13640 13641 13642 Slider.prototype.handleMouseUp = function handleMouseUp() { 13643 var doc = this.bar.el_.ownerDocument; 13644 13645 unblockTextSelection(); 13646 13647 this.removeClass('vjs-sliding'); 13648 /** 13649 * Triggered when the slider is no longer in an active state. 13650 * 13651 * @event Slider#sliderinactive 13652 * @type {EventTarget~Event} 13653 */ 13654 this.trigger('sliderinactive'); 13655 13656 this.off(doc, 'mousemove', this.handleMouseMove); 13657 this.off(doc, 'mouseup', this.handleMouseUp); 13658 this.off(doc, 'touchmove', this.handleMouseMove); 13659 this.off(doc, 'touchend', this.handleMouseUp); 13660 13661 this.update(); 13662 }; 13663 13664 /** 13665 * Update the progress bar of the `Slider`. 13666 * 13667 * @returns {number} 13668 * The percentage of progress the progress bar represents as a 13669 * number from 0 to 1. 13670 */ 13671 13672 13673 Slider.prototype.update = function update() { 13674 13675 // In VolumeBar init we have a setTimeout for update that pops and update 13676 // to the end of the execution stack. The player is destroyed before then 13677 // update will cause an error 13678 if (!this.el_) { 13679 return; 13680 } 13681 13682 // If scrubbing, we could use a cached value to make the handle keep up 13683 // with the user's mouse. On HTML5 browsers scrubbing is really smooth, but 13684 // some flash players are slow, so we might want to utilize this later. 13685 // var progress = (this.player_.scrubbing()) ? this.player_.getCache().currentTime / this.player_.duration() : this.player_.currentTime() / this.player_.duration(); 13686 var progress = this.getPercent(); 13687 var bar = this.bar; 13688 13689 // If there's no bar... 13690 if (!bar) { 13691 return; 13692 } 13693 13694 // Protect against no duration and other division issues 13695 if (typeof progress !== 'number' || progress !== progress || progress < 0 || progress === Infinity) { 13696 progress = 0; 13697 } 13698 13699 // Convert to a percentage for setting 13700 var percentage = (progress * 100).toFixed(2) + '%'; 13701 var style = bar.el().style; 13702 13703 // Set the new bar width or height 13704 if (this.vertical()) { 13705 style.height = percentage; 13706 } else { 13707 style.width = percentage; 13708 } 13709 13710 return progress; 13711 }; 13712 13713 /** 13714 * Calculate distance for slider 13715 * 13716 * @param {EventTarget~Event} event 13717 * The event that caused this function to run. 13718 * 13719 * @return {number} 13720 * The current position of the Slider. 13721 * - postition.x for vertical `Slider`s 13722 * - postition.y for horizontal `Slider`s 13723 */ 13724 13725 13726 Slider.prototype.calculateDistance = function calculateDistance(event) { 13727 var position = getPointerPosition(this.el_, event); 13728 13729 if (this.vertical()) { 13730 return position.y; 13731 } 13732 return position.x; 13733 }; 13734 13735 /** 13736 * Handle a `focus` event on this `Slider`. 13737 * 13738 * @param {EventTarget~Event} event 13739 * The `focus` event that caused this function to run. 13740 * 13741 * @listens focus 13742 */ 13743 13744 13745 Slider.prototype.handleFocus = function handleFocus() { 13746 this.on(this.bar.el_.ownerDocument, 'keydown', this.handleKeyPress); 13747 }; 13748 13749 /** 13750 * Handle a `keydown` event on the `Slider`. Watches for left, rigth, up, and down 13751 * arrow keys. This function will only be called when the slider has focus. See 13752 * {@link Slider#handleFocus} and {@link Slider#handleBlur}. 13753 * 13754 * @param {EventTarget~Event} event 13755 * the `keydown` event that caused this function to run. 13756 * 13757 * @listens keydown 13758 */ 13759 13760 13761 Slider.prototype.handleKeyPress = function handleKeyPress(event) { 13762 // Left and Down Arrows 13763 if (event.which === 37 || event.which === 40) { 13764 event.preventDefault(); 13765 this.stepBack(); 13766 13767 // Up and Right Arrows 13768 } else if (event.which === 38 || event.which === 39) { 13769 event.preventDefault(); 13770 this.stepForward(); 13771 } 13772 }; 13773 13774 /** 13775 * Handle a `blur` event on this `Slider`. 13776 * 13777 * @param {EventTarget~Event} event 13778 * The `blur` event that caused this function to run. 13779 * 13780 * @listens blur 13781 */ 13782 13783 Slider.prototype.handleBlur = function handleBlur() { 13784 this.off(this.bar.el_.ownerDocument, 'keydown', this.handleKeyPress); 13785 }; 13786 13787 /** 13788 * Listener for click events on slider, used to prevent clicks 13789 * from bubbling up to parent elements like button menus. 13790 * 13791 * @param {Object} event 13792 * Event that caused this object to run 13793 */ 13794 13795 13796 Slider.prototype.handleClick = function handleClick(event) { 13797 event.stopImmediatePropagation(); 13798 event.preventDefault(); 13799 }; 13800 13801 /** 13802 * Get/set if slider is horizontal for vertical 13803 * 13804 * @param {boolean} [bool] 13805 * - true if slider is vertical, 13806 * - false is horizontal 13807 * 13808 * @return {boolean} 13809 * - true if slider is vertical, and getting 13810 * - false if the slider is horizontal, and getting 13811 */ 13812 13813 13814 Slider.prototype.vertical = function vertical(bool) { 13815 if (bool === undefined) { 13816 return this.vertical_ || false; 13817 } 13818 13819 this.vertical_ = !!bool; 13820 13821 if (this.vertical_) { 13822 this.addClass('vjs-slider-vertical'); 13823 } else { 13824 this.addClass('vjs-slider-horizontal'); 13825 } 13826 }; 13827 13828 return Slider; 13829}(Component); 13830 13831Component.registerComponent('Slider', Slider); 13832 13833/** 13834 * @file load-progress-bar.js 13835 */ 13836/** 13837 * Shows loading progress 13838 * 13839 * @extends Component 13840 */ 13841 13842var LoadProgressBar = function (_Component) { 13843 inherits(LoadProgressBar, _Component); 13844 13845 /** 13846 * Creates an instance of this class. 13847 * 13848 * @param {Player} player 13849 * The `Player` that this class should be attached to. 13850 * 13851 * @param {Object} [options] 13852 * The key/value store of player options. 13853 */ 13854 function LoadProgressBar(player, options) { 13855 classCallCheck(this, LoadProgressBar); 13856 13857 var _this = possibleConstructorReturn(this, _Component.call(this, player, options)); 13858 13859 _this.partEls_ = []; 13860 _this.on(player, 'progress', _this.update); 13861 return _this; 13862 } 13863 13864 /** 13865 * Create the `Component`'s DOM element 13866 * 13867 * @return {Element} 13868 * The element that was created. 13869 */ 13870 13871 13872 LoadProgressBar.prototype.createEl = function createEl$$1() { 13873 return _Component.prototype.createEl.call(this, 'div', { 13874 className: 'vjs-load-progress', 13875 innerHTML: '<span class="vjs-control-text"><span>' + this.localize('Loaded') + '</span>: 0%</span>' 13876 }); 13877 }; 13878 13879 LoadProgressBar.prototype.dispose = function dispose() { 13880 this.partEls_ = null; 13881 13882 _Component.prototype.dispose.call(this); 13883 }; 13884 13885 /** 13886 * Update progress bar 13887 * 13888 * @param {EventTarget~Event} [event] 13889 * The `progress` event that caused this function to run. 13890 * 13891 * @listens Player#progress 13892 */ 13893 13894 13895 LoadProgressBar.prototype.update = function update(event) { 13896 var buffered = this.player_.buffered(); 13897 var duration = this.player_.duration(); 13898 var bufferedEnd = this.player_.bufferedEnd(); 13899 var children = this.partEls_; 13900 13901 // get the percent width of a time compared to the total end 13902 var percentify = function percentify(time, end) { 13903 // no NaN 13904 var percent = time / end || 0; 13905 13906 return (percent >= 1 ? 1 : percent) * 100 + '%'; 13907 }; 13908 13909 // update the width of the progress bar 13910 this.el_.style.width = percentify(bufferedEnd, duration); 13911 13912 // add child elements to represent the individual buffered time ranges 13913 for (var i = 0; i < buffered.length; i++) { 13914 var start = buffered.start(i); 13915 var end = buffered.end(i); 13916 var part = children[i]; 13917 13918 if (!part) { 13919 part = this.el_.appendChild(createEl()); 13920 children[i] = part; 13921 } 13922 13923 // set the percent based on the width of the progress bar (bufferedEnd) 13924 part.style.left = percentify(start, bufferedEnd); 13925 part.style.width = percentify(end - start, bufferedEnd); 13926 } 13927 13928 // remove unused buffered range elements 13929 for (var _i = children.length; _i > buffered.length; _i--) { 13930 this.el_.removeChild(children[_i - 1]); 13931 } 13932 children.length = buffered.length; 13933 }; 13934 13935 return LoadProgressBar; 13936}(Component); 13937 13938Component.registerComponent('LoadProgressBar', LoadProgressBar); 13939 13940/** 13941 * @file time-tooltip.js 13942 */ 13943/** 13944 * Time tooltips display a time above the progress bar. 13945 * 13946 * @extends Component 13947 */ 13948 13949var TimeTooltip = function (_Component) { 13950 inherits(TimeTooltip, _Component); 13951 13952 function TimeTooltip() { 13953 classCallCheck(this, TimeTooltip); 13954 return possibleConstructorReturn(this, _Component.apply(this, arguments)); 13955 } 13956 13957 /** 13958 * Create the time tooltip DOM element 13959 * 13960 * @return {Element} 13961 * The element that was created. 13962 */ 13963 TimeTooltip.prototype.createEl = function createEl$$1() { 13964 return _Component.prototype.createEl.call(this, 'div', { 13965 className: 'vjs-time-tooltip' 13966 }); 13967 }; 13968 13969 /** 13970 * Updates the position of the time tooltip relative to the `SeekBar`. 13971 * 13972 * @param {Object} seekBarRect 13973 * The `ClientRect` for the {@link SeekBar} element. 13974 * 13975 * @param {number} seekBarPoint 13976 * A number from 0 to 1, representing a horizontal reference point 13977 * from the left edge of the {@link SeekBar} 13978 */ 13979 13980 13981 TimeTooltip.prototype.update = function update(seekBarRect, seekBarPoint, content) { 13982 var tooltipRect = getBoundingClientRect(this.el_); 13983 var playerRect = getBoundingClientRect(this.player_.el()); 13984 var seekBarPointPx = seekBarRect.width * seekBarPoint; 13985 13986 // do nothing if either rect isn't available 13987 // for example, if the player isn't in the DOM for testing 13988 if (!playerRect || !tooltipRect) { 13989 return; 13990 } 13991 13992 // This is the space left of the `seekBarPoint` available within the bounds 13993 // of the player. We calculate any gap between the left edge of the player 13994 // and the left edge of the `SeekBar` and add the number of pixels in the 13995 // `SeekBar` before hitting the `seekBarPoint` 13996 var spaceLeftOfPoint = seekBarRect.left - playerRect.left + seekBarPointPx; 13997 13998 // This is the space right of the `seekBarPoint` available within the bounds 13999 // of the player. We calculate the number of pixels from the `seekBarPoint` 14000 // to the right edge of the `SeekBar` and add to that any gap between the 14001 // right edge of the `SeekBar` and the player. 14002 var spaceRightOfPoint = seekBarRect.width - seekBarPointPx + (playerRect.right - seekBarRect.right); 14003 14004 // This is the number of pixels by which the tooltip will need to be pulled 14005 // further to the right to center it over the `seekBarPoint`. 14006 var pullTooltipBy = tooltipRect.width / 2; 14007 14008 // Adjust the `pullTooltipBy` distance to the left or right depending on 14009 // the results of the space calculations above. 14010 if (spaceLeftOfPoint < pullTooltipBy) { 14011 pullTooltipBy += pullTooltipBy - spaceLeftOfPoint; 14012 } else if (spaceRightOfPoint < pullTooltipBy) { 14013 pullTooltipBy = spaceRightOfPoint; 14014 } 14015 14016 // Due to the imprecision of decimal/ratio based calculations and varying 14017 // rounding behaviors, there are cases where the spacing adjustment is off 14018 // by a pixel or two. This adds insurance to these calculations. 14019 if (pullTooltipBy < 0) { 14020 pullTooltipBy = 0; 14021 } else if (pullTooltipBy > tooltipRect.width) { 14022 pullTooltipBy = tooltipRect.width; 14023 } 14024 14025 this.el_.style.right = '-' + pullTooltipBy + 'px'; 14026 textContent(this.el_, content); 14027 }; 14028 14029 return TimeTooltip; 14030}(Component); 14031 14032Component.registerComponent('TimeTooltip', TimeTooltip); 14033 14034/** 14035 * @file play-progress-bar.js 14036 */ 14037/** 14038 * Used by {@link SeekBar} to display media playback progress as part of the 14039 * {@link ProgressControl}. 14040 * 14041 * @extends Component 14042 */ 14043 14044var PlayProgressBar = function (_Component) { 14045 inherits(PlayProgressBar, _Component); 14046 14047 function PlayProgressBar() { 14048 classCallCheck(this, PlayProgressBar); 14049 return possibleConstructorReturn(this, _Component.apply(this, arguments)); 14050 } 14051 14052 /** 14053 * Create the the DOM element for this class. 14054 * 14055 * @return {Element} 14056 * The element that was created. 14057 */ 14058 PlayProgressBar.prototype.createEl = function createEl() { 14059 return _Component.prototype.createEl.call(this, 'div', { 14060 className: 'vjs-play-progress vjs-slider-bar', 14061 innerHTML: '<span class="vjs-control-text"><span>' + this.localize('Progress') + '</span>: 0%</span>' 14062 }); 14063 }; 14064 14065 /** 14066 * Enqueues updates to its own DOM as well as the DOM of its 14067 * {@link TimeTooltip} child. 14068 * 14069 * @param {Object} seekBarRect 14070 * The `ClientRect` for the {@link SeekBar} element. 14071 * 14072 * @param {number} seekBarPoint 14073 * A number from 0 to 1, representing a horizontal reference point 14074 * from the left edge of the {@link SeekBar} 14075 */ 14076 14077 14078 PlayProgressBar.prototype.update = function update(seekBarRect, seekBarPoint) { 14079 var _this2 = this; 14080 14081 // If there is an existing rAF ID, cancel it so we don't over-queue. 14082 if (this.rafId_) { 14083 this.cancelAnimationFrame(this.rafId_); 14084 } 14085 14086 this.rafId_ = this.requestAnimationFrame(function () { 14087 var time = _this2.player_.scrubbing() ? _this2.player_.getCache().currentTime : _this2.player_.currentTime(); 14088 14089 var content = formatTime(time, _this2.player_.duration()); 14090 var timeTooltip = _this2.getChild('timeTooltip'); 14091 14092 if (timeTooltip) { 14093 timeTooltip.update(seekBarRect, seekBarPoint, content); 14094 } 14095 }); 14096 }; 14097 14098 return PlayProgressBar; 14099}(Component); 14100 14101/** 14102 * Default options for {@link PlayProgressBar}. 14103 * 14104 * @type {Object} 14105 * @private 14106 */ 14107 14108 14109PlayProgressBar.prototype.options_ = { 14110 children: [] 14111}; 14112 14113// Time tooltips should not be added to a player on mobile devices or IE8 14114if ((!IE_VERSION || IE_VERSION > 8) && !IS_IOS && !IS_ANDROID) { 14115 PlayProgressBar.prototype.options_.children.push('timeTooltip'); 14116} 14117 14118Component.registerComponent('PlayProgressBar', PlayProgressBar); 14119 14120/** 14121 * @file mouse-time-display.js 14122 */ 14123/** 14124 * The {@link MouseTimeDisplay} component tracks mouse movement over the 14125 * {@link ProgressControl}. It displays an indicator and a {@link TimeTooltip} 14126 * indicating the time which is represented by a given point in the 14127 * {@link ProgressControl}. 14128 * 14129 * @extends Component 14130 */ 14131 14132var MouseTimeDisplay = function (_Component) { 14133 inherits(MouseTimeDisplay, _Component); 14134 14135 /** 14136 * Creates an instance of this class. 14137 * 14138 * @param {Player} player 14139 * The {@link Player} that this class should be attached to. 14140 * 14141 * @param {Object} [options] 14142 * The key/value store of player options. 14143 */ 14144 function MouseTimeDisplay(player, options) { 14145 classCallCheck(this, MouseTimeDisplay); 14146 14147 var _this = possibleConstructorReturn(this, _Component.call(this, player, options)); 14148 14149 _this.update = throttle(bind(_this, _this.update), 25); 14150 return _this; 14151 } 14152 14153 /** 14154 * Create the DOM element for this class. 14155 * 14156 * @return {Element} 14157 * The element that was created. 14158 */ 14159 14160 14161 MouseTimeDisplay.prototype.createEl = function createEl() { 14162 return _Component.prototype.createEl.call(this, 'div', { 14163 className: 'vjs-mouse-display' 14164 }); 14165 }; 14166 14167 /** 14168 * Enqueues updates to its own DOM as well as the DOM of its 14169 * {@link TimeTooltip} child. 14170 * 14171 * @param {Object} seekBarRect 14172 * The `ClientRect` for the {@link SeekBar} element. 14173 * 14174 * @param {number} seekBarPoint 14175 * A number from 0 to 1, representing a horizontal reference point 14176 * from the left edge of the {@link SeekBar} 14177 */ 14178 14179 14180 MouseTimeDisplay.prototype.update = function update(seekBarRect, seekBarPoint) { 14181 var _this2 = this; 14182 14183 // If there is an existing rAF ID, cancel it so we don't over-queue. 14184 if (this.rafId_) { 14185 this.cancelAnimationFrame(this.rafId_); 14186 } 14187 14188 this.rafId_ = this.requestAnimationFrame(function () { 14189 var duration = _this2.player_.duration(); 14190 var content = formatTime(seekBarPoint * duration, duration); 14191 14192 _this2.el_.style.left = seekBarRect.width * seekBarPoint + 'px'; 14193 _this2.getChild('timeTooltip').update(seekBarRect, seekBarPoint, content); 14194 }); 14195 }; 14196 14197 return MouseTimeDisplay; 14198}(Component); 14199 14200/** 14201 * Default options for `MouseTimeDisplay` 14202 * 14203 * @type {Object} 14204 * @private 14205 */ 14206 14207 14208MouseTimeDisplay.prototype.options_ = { 14209 children: ['timeTooltip'] 14210}; 14211 14212Component.registerComponent('MouseTimeDisplay', MouseTimeDisplay); 14213 14214/** 14215 * @file seek-bar.js 14216 */ 14217// The number of seconds the `step*` functions move the timeline. 14218var STEP_SECONDS = 5; 14219 14220// The interval at which the bar should update as it progresses. 14221var UPDATE_REFRESH_INTERVAL = 30; 14222 14223/** 14224 * Seek bar and container for the progress bars. Uses {@link PlayProgressBar} 14225 * as its `bar`. 14226 * 14227 * @extends Slider 14228 */ 14229 14230var SeekBar = function (_Slider) { 14231 inherits(SeekBar, _Slider); 14232 14233 /** 14234 * Creates an instance of this class. 14235 * 14236 * @param {Player} player 14237 * The `Player` that this class should be attached to. 14238 * 14239 * @param {Object} [options] 14240 * The key/value store of player options. 14241 */ 14242 function SeekBar(player, options) { 14243 classCallCheck(this, SeekBar); 14244 14245 var _this = possibleConstructorReturn(this, _Slider.call(this, player, options)); 14246 14247 _this.update = throttle(bind(_this, _this.update), UPDATE_REFRESH_INTERVAL); 14248 14249 _this.on(player, 'timeupdate', _this.update); 14250 _this.on(player, 'ended', _this.handleEnded); 14251 14252 // when playing, let's ensure we smoothly update the play progress bar 14253 // via an interval 14254 _this.updateInterval = null; 14255 14256 _this.on(player, ['playing'], function () { 14257 _this.clearInterval(_this.updateInterval); 14258 14259 _this.updateInterval = _this.setInterval(function () { 14260 _this.requestAnimationFrame(function () { 14261 _this.update(); 14262 }); 14263 }, UPDATE_REFRESH_INTERVAL); 14264 }); 14265 14266 _this.on(player, ['ended', 'pause', 'waiting'], function () { 14267 _this.clearInterval(_this.updateInterval); 14268 }); 14269 14270 _this.on(player, ['timeupdate', 'ended'], _this.update); 14271 return _this; 14272 } 14273 14274 /** 14275 * Create the `Component`'s DOM element 14276 * 14277 * @return {Element} 14278 * The element that was created. 14279 */ 14280 14281 14282 SeekBar.prototype.createEl = function createEl$$1() { 14283 return _Slider.prototype.createEl.call(this, 'div', { 14284 className: 'vjs-progress-holder' 14285 }, { 14286 'aria-label': this.localize('Progress Bar') 14287 }); 14288 }; 14289 14290 /** 14291 * This function updates the play progress bar and accessiblity 14292 * attributes to whatever is passed in. 14293 * 14294 * @param {number} currentTime 14295 * The currentTime value that should be used for accessiblity 14296 * 14297 * @param {number} percent 14298 * The percentage as a decimal that the bar should be filled from 0-1. 14299 * 14300 * @private 14301 */ 14302 14303 14304 SeekBar.prototype.update_ = function update_(currentTime, percent) { 14305 var duration = this.player_.duration(); 14306 14307 // machine readable value of progress bar (percentage complete) 14308 this.el_.setAttribute('aria-valuenow', (percent * 100).toFixed(2)); 14309 14310 // human readable value of progress bar (time complete) 14311 this.el_.setAttribute('aria-valuetext', this.localize('progress bar timing: currentTime={1} duration={2}', [formatTime(currentTime, duration), formatTime(duration, duration)], '{1} of {2}')); 14312 14313 // Update the `PlayProgressBar`. 14314 this.bar.update(getBoundingClientRect(this.el_), percent); 14315 }; 14316 14317 /** 14318 * Update the seek bar's UI. 14319 * 14320 * @param {EventTarget~Event} [event] 14321 * The `timeupdate` or `ended` event that caused this to run. 14322 * 14323 * @listens Player#timeupdate 14324 * 14325 * @returns {number} 14326 * The current percent at a number from 0-1 14327 */ 14328 14329 14330 SeekBar.prototype.update = function update(event) { 14331 var percent = _Slider.prototype.update.call(this); 14332 14333 this.update_(this.getCurrentTime_(), percent); 14334 return percent; 14335 }; 14336 14337 /** 14338 * Get the value of current time but allows for smooth scrubbing, 14339 * when player can't keep up. 14340 * 14341 * @return {number} 14342 * The current time value to display 14343 * 14344 * @private 14345 */ 14346 14347 14348 SeekBar.prototype.getCurrentTime_ = function getCurrentTime_() { 14349 return this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime(); 14350 }; 14351 14352 /** 14353 * We want the seek bar to be full on ended 14354 * no matter what the actual internal values are. so we force it. 14355 * 14356 * @param {EventTarget~Event} [event] 14357 * The `timeupdate` or `ended` event that caused this to run. 14358 * 14359 * @listens Player#ended 14360 */ 14361 14362 14363 SeekBar.prototype.handleEnded = function handleEnded(event) { 14364 this.update_(this.player_.duration(), 1); 14365 }; 14366 14367 /** 14368 * Get the percentage of media played so far. 14369 * 14370 * @return {number} 14371 * The percentage of media played so far (0 to 1). 14372 */ 14373 14374 14375 SeekBar.prototype.getPercent = function getPercent() { 14376 var percent = this.getCurrentTime_() / this.player_.duration(); 14377 14378 return percent >= 1 ? 1 : percent; 14379 }; 14380 14381 /** 14382 * Handle mouse down on seek bar 14383 * 14384 * @param {EventTarget~Event} event 14385 * The `mousedown` event that caused this to run. 14386 * 14387 * @listens mousedown 14388 */ 14389 14390 14391 SeekBar.prototype.handleMouseDown = function handleMouseDown(event) { 14392 if (!isSingleLeftClick(event)) { 14393 return; 14394 } 14395 14396 this.player_.scrubbing(true); 14397 14398 this.videoWasPlaying = !this.player_.paused(); 14399 this.player_.pause(); 14400 14401 _Slider.prototype.handleMouseDown.call(this, event); 14402 }; 14403 14404 /** 14405 * Handle mouse move on seek bar 14406 * 14407 * @param {EventTarget~Event} event 14408 * The `mousemove` event that caused this to run. 14409 * 14410 * @listens mousemove 14411 */ 14412 14413 14414 SeekBar.prototype.handleMouseMove = function handleMouseMove(event) { 14415 if (!isSingleLeftClick(event)) { 14416 return; 14417 } 14418 14419 var newTime = this.calculateDistance(event) * this.player_.duration(); 14420 14421 // Don't let video end while scrubbing. 14422 if (newTime === this.player_.duration()) { 14423 newTime = newTime - 0.1; 14424 } 14425 14426 // Set new time (tell player to seek to new time) 14427 this.player_.currentTime(newTime); 14428 }; 14429 14430 SeekBar.prototype.enable = function enable() { 14431 _Slider.prototype.enable.call(this); 14432 var mouseTimeDisplay = this.getChild('mouseTimeDisplay'); 14433 14434 if (!mouseTimeDisplay) { 14435 return; 14436 } 14437 14438 mouseTimeDisplay.show(); 14439 }; 14440 14441 SeekBar.prototype.disable = function disable() { 14442 _Slider.prototype.disable.call(this); 14443 var mouseTimeDisplay = this.getChild('mouseTimeDisplay'); 14444 14445 if (!mouseTimeDisplay) { 14446 return; 14447 } 14448 14449 mouseTimeDisplay.hide(); 14450 }; 14451 14452 /** 14453 * Handle mouse up on seek bar 14454 * 14455 * @param {EventTarget~Event} event 14456 * The `mouseup` event that caused this to run. 14457 * 14458 * @listens mouseup 14459 */ 14460 14461 14462 SeekBar.prototype.handleMouseUp = function handleMouseUp(event) { 14463 _Slider.prototype.handleMouseUp.call(this, event); 14464 14465 this.player_.scrubbing(false); 14466 14467 /** 14468 * Trigger timeupdate because we're done seeking and the time has changed. 14469 * This is particularly useful for if the player is paused to time the time displays. 14470 * 14471 * @event Tech#timeupdate 14472 * @type {EventTarget~Event} 14473 */ 14474 this.player_.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true }); 14475 if (this.videoWasPlaying) { 14476 silencePromise(this.player_.play()); 14477 } 14478 }; 14479 14480 /** 14481 * Move more quickly fast forward for keyboard-only users 14482 */ 14483 14484 14485 SeekBar.prototype.stepForward = function stepForward() { 14486 this.player_.currentTime(this.player_.currentTime() + STEP_SECONDS); 14487 }; 14488 14489 /** 14490 * Move more quickly rewind for keyboard-only users 14491 */ 14492 14493 14494 SeekBar.prototype.stepBack = function stepBack() { 14495 this.player_.currentTime(this.player_.currentTime() - STEP_SECONDS); 14496 }; 14497 14498 /** 14499 * Toggles the playback state of the player 14500 * This gets called when enter or space is used on the seekbar 14501 * 14502 * @param {EventTarget~Event} event 14503 * The `keydown` event that caused this function to be called 14504 * 14505 */ 14506 14507 14508 SeekBar.prototype.handleAction = function handleAction(event) { 14509 if (this.player_.paused()) { 14510 this.player_.play(); 14511 } else { 14512 this.player_.pause(); 14513 } 14514 }; 14515 14516 /** 14517 * Called when this SeekBar has focus and a key gets pressed down. By 14518 * default it will call `this.handleAction` when the key is space or enter. 14519 * 14520 * @param {EventTarget~Event} event 14521 * The `keydown` event that caused this function to be called. 14522 * 14523 * @listens keydown 14524 */ 14525 14526 14527 SeekBar.prototype.handleKeyPress = function handleKeyPress(event) { 14528 14529 // Support Space (32) or Enter (13) key operation to fire a click event 14530 if (event.which === 32 || event.which === 13) { 14531 event.preventDefault(); 14532 this.handleAction(event); 14533 } else if (_Slider.prototype.handleKeyPress) { 14534 14535 // Pass keypress handling up for unsupported keys 14536 _Slider.prototype.handleKeyPress.call(this, event); 14537 } 14538 }; 14539 14540 return SeekBar; 14541}(Slider); 14542 14543/** 14544 * Default options for the `SeekBar` 14545 * 14546 * @type {Object} 14547 * @private 14548 */ 14549 14550 14551SeekBar.prototype.options_ = { 14552 children: ['loadProgressBar', 'playProgressBar'], 14553 barName: 'playProgressBar' 14554}; 14555 14556// MouseTimeDisplay tooltips should not be added to a player on mobile devices or IE8 14557if ((!IE_VERSION || IE_VERSION > 8) && !IS_IOS && !IS_ANDROID) { 14558 SeekBar.prototype.options_.children.splice(1, 0, 'mouseTimeDisplay'); 14559} 14560 14561/** 14562 * Call the update event for this Slider when this event happens on the player. 14563 * 14564 * @type {string} 14565 */ 14566SeekBar.prototype.playerEvent = 'timeupdate'; 14567 14568Component.registerComponent('SeekBar', SeekBar); 14569 14570/** 14571 * @file progress-control.js 14572 */ 14573/** 14574 * The Progress Control component contains the seek bar, load progress, 14575 * and play progress. 14576 * 14577 * @extends Component 14578 */ 14579 14580var ProgressControl = function (_Component) { 14581 inherits(ProgressControl, _Component); 14582 14583 /** 14584 * Creates an instance of this class. 14585 * 14586 * @param {Player} player 14587 * The `Player` that this class should be attached to. 14588 * 14589 * @param {Object} [options] 14590 * The key/value store of player options. 14591 */ 14592 function ProgressControl(player, options) { 14593 classCallCheck(this, ProgressControl); 14594 14595 var _this = possibleConstructorReturn(this, _Component.call(this, player, options)); 14596 14597 _this.handleMouseMove = throttle(bind(_this, _this.handleMouseMove), 25); 14598 _this.throttledHandleMouseSeek = throttle(bind(_this, _this.handleMouseSeek), 25); 14599 14600 _this.enable(); 14601 return _this; 14602 } 14603 14604 /** 14605 * Create the `Component`'s DOM element 14606 * 14607 * @return {Element} 14608 * The element that was created. 14609 */ 14610 14611 14612 ProgressControl.prototype.createEl = function createEl$$1() { 14613 return _Component.prototype.createEl.call(this, 'div', { 14614 className: 'vjs-progress-control vjs-control' 14615 }); 14616 }; 14617 14618 /** 14619 * When the mouse moves over the `ProgressControl`, the pointer position 14620 * gets passed down to the `MouseTimeDisplay` component. 14621 * 14622 * @param {EventTarget~Event} event 14623 * The `mousemove` event that caused this function to run. 14624 * 14625 * @listen mousemove 14626 */ 14627 14628 14629 ProgressControl.prototype.handleMouseMove = function handleMouseMove(event) { 14630 var seekBar = this.getChild('seekBar'); 14631 var mouseTimeDisplay = seekBar.getChild('mouseTimeDisplay'); 14632 var seekBarEl = seekBar.el(); 14633 var seekBarRect = getBoundingClientRect(seekBarEl); 14634 var seekBarPoint = getPointerPosition(seekBarEl, event).x; 14635 14636 // The default skin has a gap on either side of the `SeekBar`. This means 14637 // that it's possible to trigger this behavior outside the boundaries of 14638 // the `SeekBar`. This ensures we stay within it at all times. 14639 if (seekBarPoint > 1) { 14640 seekBarPoint = 1; 14641 } else if (seekBarPoint < 0) { 14642 seekBarPoint = 0; 14643 } 14644 14645 if (mouseTimeDisplay) { 14646 mouseTimeDisplay.update(seekBarRect, seekBarPoint); 14647 } 14648 }; 14649 14650 /** 14651 * A throttled version of the {@link ProgressControl#handleMouseSeek} listener. 14652 * 14653 * @method ProgressControl#throttledHandleMouseSeek 14654 * @param {EventTarget~Event} event 14655 * The `mousemove` event that caused this function to run. 14656 * 14657 * @listen mousemove 14658 * @listen touchmove 14659 */ 14660 14661 /** 14662 * Handle `mousemove` or `touchmove` events on the `ProgressControl`. 14663 * 14664 * @param {EventTarget~Event} event 14665 * `mousedown` or `touchstart` event that triggered this function 14666 * 14667 * @listens mousemove 14668 * @listens touchmove 14669 */ 14670 14671 14672 ProgressControl.prototype.handleMouseSeek = function handleMouseSeek(event) { 14673 var seekBar = this.getChild('seekBar'); 14674 14675 seekBar.handleMouseMove(event); 14676 }; 14677 14678 /** 14679 * Are controls are currently enabled for this progress control. 14680 * 14681 * @return {boolean} 14682 * true if controls are enabled, false otherwise 14683 */ 14684 14685 14686 ProgressControl.prototype.enabled = function enabled() { 14687 return this.enabled_; 14688 }; 14689 14690 /** 14691 * Disable all controls on the progress control and its children 14692 */ 14693 14694 14695 ProgressControl.prototype.disable = function disable() { 14696 this.children().forEach(function (child) { 14697 return child.disable && child.disable(); 14698 }); 14699 14700 if (!this.enabled()) { 14701 return; 14702 } 14703 14704 this.off(['mousedown', 'touchstart'], this.handleMouseDown); 14705 this.off(this.el_, 'mousemove', this.handleMouseMove); 14706 this.handleMouseUp(); 14707 14708 this.addClass('disabled'); 14709 14710 this.enabled_ = false; 14711 }; 14712 14713 /** 14714 * Enable all controls on the progress control and its children 14715 */ 14716 14717 14718 ProgressControl.prototype.enable = function enable() { 14719 this.children().forEach(function (child) { 14720 return child.enable && child.enable(); 14721 }); 14722 14723 if (this.enabled()) { 14724 return; 14725 } 14726 14727 this.on(['mousedown', 'touchstart'], this.handleMouseDown); 14728 this.on(this.el_, 'mousemove', this.handleMouseMove); 14729 this.removeClass('disabled'); 14730 14731 this.enabled_ = true; 14732 }; 14733 14734 /** 14735 * Handle `mousedown` or `touchstart` events on the `ProgressControl`. 14736 * 14737 * @param {EventTarget~Event} event 14738 * `mousedown` or `touchstart` event that triggered this function 14739 * 14740 * @listens mousedown 14741 * @listens touchstart 14742 */ 14743 14744 14745 ProgressControl.prototype.handleMouseDown = function handleMouseDown(event) { 14746 var doc = this.el_.ownerDocument; 14747 14748 this.on(doc, 'mousemove', this.throttledHandleMouseSeek); 14749 this.on(doc, 'touchmove', this.throttledHandleMouseSeek); 14750 this.on(doc, 'mouseup', this.handleMouseUp); 14751 this.on(doc, 'touchend', this.handleMouseUp); 14752 }; 14753 14754 /** 14755 * Handle `mouseup` or `touchend` events on the `ProgressControl`. 14756 * 14757 * @param {EventTarget~Event} event 14758 * `mouseup` or `touchend` event that triggered this function. 14759 * 14760 * @listens touchend 14761 * @listens mouseup 14762 */ 14763 14764 14765 ProgressControl.prototype.handleMouseUp = function handleMouseUp(event) { 14766 var doc = this.el_.ownerDocument; 14767 14768 this.off(doc, 'mousemove', this.throttledHandleMouseSeek); 14769 this.off(doc, 'touchmove', this.throttledHandleMouseSeek); 14770 this.off(doc, 'mouseup', this.handleMouseUp); 14771 this.off(doc, 'touchend', this.handleMouseUp); 14772 }; 14773 14774 return ProgressControl; 14775}(Component); 14776 14777/** 14778 * Default options for `ProgressControl` 14779 * 14780 * @type {Object} 14781 * @private 14782 */ 14783 14784 14785ProgressControl.prototype.options_ = { 14786 children: ['seekBar'] 14787}; 14788 14789Component.registerComponent('ProgressControl', ProgressControl); 14790 14791/** 14792 * @file fullscreen-toggle.js 14793 */ 14794/** 14795 * Toggle fullscreen video 14796 * 14797 * @extends Button 14798 */ 14799 14800var FullscreenToggle = function (_Button) { 14801 inherits(FullscreenToggle, _Button); 14802 14803 /** 14804 * Creates an instance of this class. 14805 * 14806 * @param {Player} player 14807 * The `Player` that this class should be attached to. 14808 * 14809 * @param {Object} [options] 14810 * The key/value store of player options. 14811 */ 14812 function FullscreenToggle(player, options) { 14813 classCallCheck(this, FullscreenToggle); 14814 14815 var _this = possibleConstructorReturn(this, _Button.call(this, player, options)); 14816 14817 _this.on(player, 'fullscreenchange', _this.handleFullscreenChange); 14818 return _this; 14819 } 14820 14821 /** 14822 * Builds the default DOM `className`. 14823 * 14824 * @return {string} 14825 * The DOM `className` for this object. 14826 */ 14827 14828 14829 FullscreenToggle.prototype.buildCSSClass = function buildCSSClass() { 14830 return 'vjs-fullscreen-control ' + _Button.prototype.buildCSSClass.call(this); 14831 }; 14832 14833 /** 14834 * Handles fullscreenchange on the player and change control text accordingly. 14835 * 14836 * @param {EventTarget~Event} [event] 14837 * The {@link Player#fullscreenchange} event that caused this function to be 14838 * called. 14839 * 14840 * @listens Player#fullscreenchange 14841 */ 14842 14843 14844 FullscreenToggle.prototype.handleFullscreenChange = function handleFullscreenChange(event) { 14845 if (this.player_.isFullscreen()) { 14846 this.controlText('Non-Fullscreen'); 14847 } else { 14848 this.controlText('Fullscreen'); 14849 } 14850 }; 14851 14852 /** 14853 * This gets called when an `FullscreenToggle` is "clicked". See 14854 * {@link ClickableComponent} for more detailed information on what a click can be. 14855 * 14856 * @param {EventTarget~Event} [event] 14857 * The `keydown`, `tap`, or `click` event that caused this function to be 14858 * called. 14859 * 14860 * @listens tap 14861 * @listens click 14862 */ 14863 14864 14865 FullscreenToggle.prototype.handleClick = function handleClick(event) { 14866 if (!this.player_.isFullscreen()) { 14867 this.player_.requestFullscreen(); 14868 } else { 14869 this.player_.exitFullscreen(); 14870 } 14871 }; 14872 14873 return FullscreenToggle; 14874}(Button); 14875 14876/** 14877 * The text that should display over the `FullscreenToggle`s controls. Added for localization. 14878 * 14879 * @type {string} 14880 * @private 14881 */ 14882 14883 14884FullscreenToggle.prototype.controlText_ = 'Fullscreen'; 14885 14886Component.registerComponent('FullscreenToggle', FullscreenToggle); 14887 14888/** 14889 * Check if volume control is supported and if it isn't hide the 14890 * `Component` that was passed using the `vjs-hidden` class. 14891 * 14892 * @param {Component} self 14893 * The component that should be hidden if volume is unsupported 14894 * 14895 * @param {Player} player 14896 * A reference to the player 14897 * 14898 * @private 14899 */ 14900var checkVolumeSupport = function checkVolumeSupport(self, player) { 14901 // hide volume controls when they're not supported by the current tech 14902 if (player.tech_ && !player.tech_.featuresVolumeControl) { 14903 self.addClass('vjs-hidden'); 14904 } 14905 14906 self.on(player, 'loadstart', function () { 14907 if (!player.tech_.featuresVolumeControl) { 14908 self.addClass('vjs-hidden'); 14909 } else { 14910 self.removeClass('vjs-hidden'); 14911 } 14912 }); 14913}; 14914 14915/** 14916 * @file volume-level.js 14917 */ 14918/** 14919 * Shows volume level 14920 * 14921 * @extends Component 14922 */ 14923 14924var VolumeLevel = function (_Component) { 14925 inherits(VolumeLevel, _Component); 14926 14927 function VolumeLevel() { 14928 classCallCheck(this, VolumeLevel); 14929 return possibleConstructorReturn(this, _Component.apply(this, arguments)); 14930 } 14931 14932 /** 14933 * Create the `Component`'s DOM element 14934 * 14935 * @return {Element} 14936 * The element that was created. 14937 */ 14938 VolumeLevel.prototype.createEl = function createEl() { 14939 return _Component.prototype.createEl.call(this, 'div', { 14940 className: 'vjs-volume-level', 14941 innerHTML: '<span class="vjs-control-text"></span>' 14942 }); 14943 }; 14944 14945 return VolumeLevel; 14946}(Component); 14947 14948Component.registerComponent('VolumeLevel', VolumeLevel); 14949 14950/** 14951 * @file volume-bar.js 14952 */ 14953// Required children 14954/** 14955 * The bar that contains the volume level and can be clicked on to adjust the level 14956 * 14957 * @extends Slider 14958 */ 14959 14960var VolumeBar = function (_Slider) { 14961 inherits(VolumeBar, _Slider); 14962 14963 /** 14964 * Creates an instance of this class. 14965 * 14966 * @param {Player} player 14967 * The `Player` that this class should be attached to. 14968 * 14969 * @param {Object} [options] 14970 * The key/value store of player options. 14971 */ 14972 function VolumeBar(player, options) { 14973 classCallCheck(this, VolumeBar); 14974 14975 var _this = possibleConstructorReturn(this, _Slider.call(this, player, options)); 14976 14977 _this.on('slideractive', _this.updateLastVolume_); 14978 _this.on(player, 'volumechange', _this.updateARIAAttributes); 14979 player.ready(function () { 14980 return _this.updateARIAAttributes(); 14981 }); 14982 return _this; 14983 } 14984 14985 /** 14986 * Create the `Component`'s DOM element 14987 * 14988 * @return {Element} 14989 * The element that was created. 14990 */ 14991 14992 14993 VolumeBar.prototype.createEl = function createEl$$1() { 14994 return _Slider.prototype.createEl.call(this, 'div', { 14995 className: 'vjs-volume-bar vjs-slider-bar' 14996 }, { 14997 'aria-label': this.localize('Volume Level'), 14998 'aria-live': 'polite' 14999 }); 15000 }; 15001 15002 /** 15003 * Handle mouse down on volume bar 15004 * 15005 * @param {EventTarget~Event} event 15006 * The `mousedown` event that caused this to run. 15007 * 15008 * @listens mousedown 15009 */ 15010 15011 15012 VolumeBar.prototype.handleMouseDown = function handleMouseDown(event) { 15013 if (!isSingleLeftClick(event)) { 15014 return; 15015 } 15016 15017 _Slider.prototype.handleMouseDown.call(this, event); 15018 }; 15019 15020 /** 15021 * Handle movement events on the {@link VolumeMenuButton}. 15022 * 15023 * @param {EventTarget~Event} event 15024 * The event that caused this function to run. 15025 * 15026 * @listens mousemove 15027 */ 15028 15029 15030 VolumeBar.prototype.handleMouseMove = function handleMouseMove(event) { 15031 if (!isSingleLeftClick(event)) { 15032 return; 15033 } 15034 15035 this.checkMuted(); 15036 this.player_.volume(this.calculateDistance(event)); 15037 }; 15038 15039 /** 15040 * If the player is muted unmute it. 15041 */ 15042 15043 15044 VolumeBar.prototype.checkMuted = function checkMuted() { 15045 if (this.player_.muted()) { 15046 this.player_.muted(false); 15047 } 15048 }; 15049 15050 /** 15051 * Get percent of volume level 15052 * 15053 * @return {number} 15054 * Volume level percent as a decimal number. 15055 */ 15056 15057 15058 VolumeBar.prototype.getPercent = function getPercent() { 15059 if (this.player_.muted()) { 15060 return 0; 15061 } 15062 return this.player_.volume(); 15063 }; 15064 15065 /** 15066 * Increase volume level for keyboard users 15067 */ 15068 15069 15070 VolumeBar.prototype.stepForward = function stepForward() { 15071 this.checkMuted(); 15072 this.player_.volume(this.player_.volume() + 0.1); 15073 }; 15074 15075 /** 15076 * Decrease volume level for keyboard users 15077 */ 15078 15079 15080 VolumeBar.prototype.stepBack = function stepBack() { 15081 this.checkMuted(); 15082 this.player_.volume(this.player_.volume() - 0.1); 15083 }; 15084 15085 /** 15086 * Update ARIA accessibility attributes 15087 * 15088 * @param {EventTarget~Event} [event] 15089 * The `volumechange` event that caused this function to run. 15090 * 15091 * @listens Player#volumechange 15092 */ 15093 15094 15095 VolumeBar.prototype.updateARIAAttributes = function updateARIAAttributes(event) { 15096 var ariaValue = this.player_.muted() ? 0 : this.volumeAsPercentage_(); 15097 15098 this.el_.setAttribute('aria-valuenow', ariaValue); 15099 this.el_.setAttribute('aria-valuetext', ariaValue + '%'); 15100 }; 15101 15102 /** 15103 * Returns the current value of the player volume as a percentage 15104 * 15105 * @private 15106 */ 15107 15108 15109 VolumeBar.prototype.volumeAsPercentage_ = function volumeAsPercentage_() { 15110 return Math.round(this.player_.volume() * 100); 15111 }; 15112 15113 /** 15114 * When user starts dragging the VolumeBar, store the volume and listen for 15115 * the end of the drag. When the drag ends, if the volume was set to zero, 15116 * set lastVolume to the stored volume. 15117 * 15118 * @listens slideractive 15119 * @private 15120 */ 15121 15122 15123 VolumeBar.prototype.updateLastVolume_ = function updateLastVolume_() { 15124 var _this2 = this; 15125 15126 var volumeBeforeDrag = this.player_.volume(); 15127 15128 this.one('sliderinactive', function () { 15129 if (_this2.player_.volume() === 0) { 15130 _this2.player_.lastVolume_(volumeBeforeDrag); 15131 } 15132 }); 15133 }; 15134 15135 return VolumeBar; 15136}(Slider); 15137 15138/** 15139 * Default options for the `VolumeBar` 15140 * 15141 * @type {Object} 15142 * @private 15143 */ 15144 15145 15146VolumeBar.prototype.options_ = { 15147 children: ['volumeLevel'], 15148 barName: 'volumeLevel' 15149}; 15150 15151/** 15152 * Call the update event for this Slider when this event happens on the player. 15153 * 15154 * @type {string} 15155 */ 15156VolumeBar.prototype.playerEvent = 'volumechange'; 15157 15158Component.registerComponent('VolumeBar', VolumeBar); 15159 15160/** 15161 * @file volume-control.js 15162 */ 15163// Required children 15164/** 15165 * The component for controlling the volume level 15166 * 15167 * @extends Component 15168 */ 15169 15170var VolumeControl = function (_Component) { 15171 inherits(VolumeControl, _Component); 15172 15173 /** 15174 * Creates an instance of this class. 15175 * 15176 * @param {Player} player 15177 * The `Player` that this class should be attached to. 15178 * 15179 * @param {Object} [options={}] 15180 * The key/value store of player options. 15181 */ 15182 function VolumeControl(player) { 15183 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 15184 classCallCheck(this, VolumeControl); 15185 15186 options.vertical = options.vertical || false; 15187 15188 // Pass the vertical option down to the VolumeBar if 15189 // the VolumeBar is turned on. 15190 if (typeof options.volumeBar === 'undefined' || isPlain(options.volumeBar)) { 15191 options.volumeBar = options.volumeBar || {}; 15192 options.volumeBar.vertical = options.vertical; 15193 } 15194 15195 // hide this control if volume support is missing 15196 var _this = possibleConstructorReturn(this, _Component.call(this, player, options)); 15197 15198 checkVolumeSupport(_this, player); 15199 15200 _this.throttledHandleMouseMove = throttle(bind(_this, _this.handleMouseMove), 25); 15201 15202 _this.on('mousedown', _this.handleMouseDown); 15203 _this.on('touchstart', _this.handleMouseDown); 15204 15205 // while the slider is active (the mouse has been pressed down and 15206 // is dragging) or in focus we do not want to hide the VolumeBar 15207 _this.on(_this.volumeBar, ['focus', 'slideractive'], function () { 15208 _this.volumeBar.addClass('vjs-slider-active'); 15209 _this.addClass('vjs-slider-active'); 15210 _this.trigger('slideractive'); 15211 }); 15212 15213 _this.on(_this.volumeBar, ['blur', 'sliderinactive'], function () { 15214 _this.volumeBar.removeClass('vjs-slider-active'); 15215 _this.removeClass('vjs-slider-active'); 15216 _this.trigger('sliderinactive'); 15217 }); 15218 return _this; 15219 } 15220 15221 /** 15222 * Create the `Component`'s DOM element 15223 * 15224 * @return {Element} 15225 * The element that was created. 15226 */ 15227 15228 15229 VolumeControl.prototype.createEl = function createEl() { 15230 var orientationClass = 'vjs-volume-horizontal'; 15231 15232 if (this.options_.vertical) { 15233 orientationClass = 'vjs-volume-vertical'; 15234 } 15235 15236 return _Component.prototype.createEl.call(this, 'div', { 15237 className: 'vjs-volume-control vjs-control ' + orientationClass 15238 }); 15239 }; 15240 15241 /** 15242 * Handle `mousedown` or `touchstart` events on the `VolumeControl`. 15243 * 15244 * @param {EventTarget~Event} event 15245 * `mousedown` or `touchstart` event that triggered this function 15246 * 15247 * @listens mousedown 15248 * @listens touchstart 15249 */ 15250 15251 15252 VolumeControl.prototype.handleMouseDown = function handleMouseDown(event) { 15253 var doc = this.el_.ownerDocument; 15254 15255 this.on(doc, 'mousemove', this.throttledHandleMouseMove); 15256 this.on(doc, 'touchmove', this.throttledHandleMouseMove); 15257 this.on(doc, 'mouseup', this.handleMouseUp); 15258 this.on(doc, 'touchend', this.handleMouseUp); 15259 }; 15260 15261 /** 15262 * Handle `mouseup` or `touchend` events on the `VolumeControl`. 15263 * 15264 * @param {EventTarget~Event} event 15265 * `mouseup` or `touchend` event that triggered this function. 15266 * 15267 * @listens touchend 15268 * @listens mouseup 15269 */ 15270 15271 15272 VolumeControl.prototype.handleMouseUp = function handleMouseUp(event) { 15273 var doc = this.el_.ownerDocument; 15274 15275 this.off(doc, 'mousemove', this.throttledHandleMouseMove); 15276 this.off(doc, 'touchmove', this.throttledHandleMouseMove); 15277 this.off(doc, 'mouseup', this.handleMouseUp); 15278 this.off(doc, 'touchend', this.handleMouseUp); 15279 }; 15280 15281 /** 15282 * Handle `mousedown` or `touchstart` events on the `VolumeControl`. 15283 * 15284 * @param {EventTarget~Event} event 15285 * `mousedown` or `touchstart` event that triggered this function 15286 * 15287 * @listens mousedown 15288 * @listens touchstart 15289 */ 15290 15291 15292 VolumeControl.prototype.handleMouseMove = function handleMouseMove(event) { 15293 this.volumeBar.handleMouseMove(event); 15294 }; 15295 15296 return VolumeControl; 15297}(Component); 15298 15299/** 15300 * Default options for the `VolumeControl` 15301 * 15302 * @type {Object} 15303 * @private 15304 */ 15305 15306 15307VolumeControl.prototype.options_ = { 15308 children: ['volumeBar'] 15309}; 15310 15311Component.registerComponent('VolumeControl', VolumeControl); 15312 15313/** 15314 * @file mute-toggle.js 15315 */ 15316/** 15317 * A button component for muting the audio. 15318 * 15319 * @extends Button 15320 */ 15321 15322var MuteToggle = function (_Button) { 15323 inherits(MuteToggle, _Button); 15324 15325 /** 15326 * Creates an instance of this class. 15327 * 15328 * @param {Player} player 15329 * The `Player` that this class should be attached to. 15330 * 15331 * @param {Object} [options] 15332 * The key/value store of player options. 15333 */ 15334 function MuteToggle(player, options) { 15335 classCallCheck(this, MuteToggle); 15336 15337 // hide this control if volume support is missing 15338 var _this = possibleConstructorReturn(this, _Button.call(this, player, options)); 15339 15340 checkVolumeSupport(_this, player); 15341 15342 _this.on(player, ['loadstart', 'volumechange'], _this.update); 15343 return _this; 15344 } 15345 15346 /** 15347 * Builds the default DOM `className`. 15348 * 15349 * @return {string} 15350 * The DOM `className` for this object. 15351 */ 15352 15353 15354 MuteToggle.prototype.buildCSSClass = function buildCSSClass() { 15355 return 'vjs-mute-control ' + _Button.prototype.buildCSSClass.call(this); 15356 }; 15357 15358 /** 15359 * This gets called when an `MuteToggle` is "clicked". See 15360 * {@link ClickableComponent} for more detailed information on what a click can be. 15361 * 15362 * @param {EventTarget~Event} [event] 15363 * The `keydown`, `tap`, or `click` event that caused this function to be 15364 * called. 15365 * 15366 * @listens tap 15367 * @listens click 15368 */ 15369 15370 15371 MuteToggle.prototype.handleClick = function handleClick(event) { 15372 var vol = this.player_.volume(); 15373 var lastVolume = this.player_.lastVolume_(); 15374 15375 if (vol === 0) { 15376 var volumeToSet = lastVolume < 0.1 ? 0.1 : lastVolume; 15377 15378 this.player_.volume(volumeToSet); 15379 this.player_.muted(false); 15380 } else { 15381 this.player_.muted(this.player_.muted() ? false : true); 15382 } 15383 }; 15384 15385 /** 15386 * Update the `MuteToggle` button based on the state of `volume` and `muted` 15387 * on the player. 15388 * 15389 * @param {EventTarget~Event} [event] 15390 * The {@link Player#loadstart} event if this function was called 15391 * through an event. 15392 * 15393 * @listens Player#loadstart 15394 * @listens Player#volumechange 15395 */ 15396 15397 15398 MuteToggle.prototype.update = function update(event) { 15399 this.updateIcon_(); 15400 this.updateControlText_(); 15401 }; 15402 15403 /** 15404 * Update the appearance of the `MuteToggle` icon. 15405 * 15406 * Possible states (given `level` variable below): 15407 * - 0: crossed out 15408 * - 1: zero bars of volume 15409 * - 2: one bar of volume 15410 * - 3: two bars of volume 15411 * 15412 * @private 15413 */ 15414 15415 15416 MuteToggle.prototype.updateIcon_ = function updateIcon_() { 15417 var vol = this.player_.volume(); 15418 var level = 3; 15419 15420 if (vol === 0 || this.player_.muted()) { 15421 level = 0; 15422 } else if (vol < 0.33) { 15423 level = 1; 15424 } else if (vol < 0.67) { 15425 level = 2; 15426 } 15427 15428 // TODO improve muted icon classes 15429 for (var i = 0; i < 4; i++) { 15430 removeClass(this.el_, 'vjs-vol-' + i); 15431 } 15432 addClass(this.el_, 'vjs-vol-' + level); 15433 }; 15434 15435 /** 15436 * If `muted` has changed on the player, update the control text 15437 * (`title` attribute on `vjs-mute-control` element and content of 15438 * `vjs-control-text` element). 15439 * 15440 * @private 15441 */ 15442 15443 15444 MuteToggle.prototype.updateControlText_ = function updateControlText_() { 15445 var soundOff = this.player_.muted() || this.player_.volume() === 0; 15446 var text = soundOff ? 'Unmute' : 'Mute'; 15447 15448 if (this.controlText() !== text) { 15449 this.controlText(text); 15450 } 15451 }; 15452 15453 return MuteToggle; 15454}(Button); 15455 15456/** 15457 * The text that should display over the `MuteToggle`s controls. Added for localization. 15458 * 15459 * @type {string} 15460 * @private 15461 */ 15462 15463 15464MuteToggle.prototype.controlText_ = 'Mute'; 15465 15466Component.registerComponent('MuteToggle', MuteToggle); 15467 15468/** 15469 * @file volume-control.js 15470 */ 15471// Required children 15472/** 15473 * A Component to contain the MuteToggle and VolumeControl so that 15474 * they can work together. 15475 * 15476 * @extends Component 15477 */ 15478 15479var VolumePanel = function (_Component) { 15480 inherits(VolumePanel, _Component); 15481 15482 /** 15483 * Creates an instance of this class. 15484 * 15485 * @param {Player} player 15486 * The `Player` that this class should be attached to. 15487 * 15488 * @param {Object} [options={}] 15489 * The key/value store of player options. 15490 */ 15491 function VolumePanel(player) { 15492 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 15493 classCallCheck(this, VolumePanel); 15494 15495 if (typeof options.inline !== 'undefined') { 15496 options.inline = options.inline; 15497 } else { 15498 options.inline = true; 15499 } 15500 15501 // pass the inline option down to the VolumeControl as vertical if 15502 // the VolumeControl is on. 15503 if (typeof options.volumeControl === 'undefined' || isPlain(options.volumeControl)) { 15504 options.volumeControl = options.volumeControl || {}; 15505 options.volumeControl.vertical = !options.inline; 15506 } 15507 15508 // hide this control if volume support is missing 15509 var _this = possibleConstructorReturn(this, _Component.call(this, player, options)); 15510 15511 checkVolumeSupport(_this, player); 15512 15513 // while the slider is active (the mouse has been pressed down and 15514 // is dragging) we do not want to hide the VolumeBar 15515 _this.on(_this.volumeControl, ['slideractive'], _this.sliderActive_); 15516 15517 _this.on(_this.volumeControl, ['sliderinactive'], _this.sliderInactive_); 15518 return _this; 15519 } 15520 15521 /** 15522 * Add vjs-slider-active class to the VolumePanel 15523 * 15524 * @listens VolumeControl#slideractive 15525 * @private 15526 */ 15527 15528 15529 VolumePanel.prototype.sliderActive_ = function sliderActive_() { 15530 this.addClass('vjs-slider-active'); 15531 }; 15532 15533 /** 15534 * Removes vjs-slider-active class to the VolumePanel 15535 * 15536 * @listens VolumeControl#sliderinactive 15537 * @private 15538 */ 15539 15540 15541 VolumePanel.prototype.sliderInactive_ = function sliderInactive_() { 15542 this.removeClass('vjs-slider-active'); 15543 }; 15544 15545 /** 15546 * Create the `Component`'s DOM element 15547 * 15548 * @return {Element} 15549 * The element that was created. 15550 */ 15551 15552 15553 VolumePanel.prototype.createEl = function createEl() { 15554 var orientationClass = 'vjs-volume-panel-horizontal'; 15555 15556 if (!this.options_.inline) { 15557 orientationClass = 'vjs-volume-panel-vertical'; 15558 } 15559 15560 return _Component.prototype.createEl.call(this, 'div', { 15561 className: 'vjs-volume-panel vjs-control ' + orientationClass 15562 }); 15563 }; 15564 15565 return VolumePanel; 15566}(Component); 15567 15568/** 15569 * Default options for the `VolumeControl` 15570 * 15571 * @type {Object} 15572 * @private 15573 */ 15574 15575 15576VolumePanel.prototype.options_ = { 15577 children: ['muteToggle', 'volumeControl'] 15578}; 15579 15580Component.registerComponent('VolumePanel', VolumePanel); 15581 15582/** 15583 * @file menu.js 15584 */ 15585/** 15586 * The Menu component is used to build popup menus, including subtitle and 15587 * captions selection menus. 15588 * 15589 * @extends Component 15590 */ 15591 15592var Menu = function (_Component) { 15593 inherits(Menu, _Component); 15594 15595 /** 15596 * Create an instance of this class. 15597 * 15598 * @param {Player} player 15599 * the player that this component should attach to 15600 * 15601 * @param {Object} [options] 15602 * Object of option names and values 15603 * 15604 */ 15605 function Menu(player, options) { 15606 classCallCheck(this, Menu); 15607 15608 var _this = possibleConstructorReturn(this, _Component.call(this, player, options)); 15609 15610 if (options) { 15611 _this.menuButton_ = options.menuButton; 15612 } 15613 15614 _this.focusedChild_ = -1; 15615 15616 _this.on('keydown', _this.handleKeyPress); 15617 return _this; 15618 } 15619 15620 /** 15621 * Add a {@link MenuItem} to the menu. 15622 * 15623 * @param {Object|string} component 15624 * The name or instance of the `MenuItem` to add. 15625 * 15626 */ 15627 15628 15629 Menu.prototype.addItem = function addItem(component) { 15630 this.addChild(component); 15631 component.on('click', bind(this, function (event) { 15632 // Unpress the associated MenuButton, and move focus back to it 15633 if (this.menuButton_) { 15634 this.menuButton_.unpressButton(); 15635 15636 // don't focus menu button if item is a caption settings item 15637 // because focus will move elsewhere and it logs an error on IE8 15638 if (component.name() !== 'CaptionSettingsMenuItem') { 15639 this.menuButton_.focus(); 15640 } 15641 } 15642 })); 15643 }; 15644 15645 /** 15646 * Create the `Menu`s DOM element. 15647 * 15648 * @return {Element} 15649 * the element that was created 15650 */ 15651 15652 15653 Menu.prototype.createEl = function createEl$$1() { 15654 var contentElType = this.options_.contentElType || 'ul'; 15655 15656 this.contentEl_ = createEl(contentElType, { 15657 className: 'vjs-menu-content' 15658 }); 15659 15660 this.contentEl_.setAttribute('role', 'menu'); 15661 15662 var el = _Component.prototype.createEl.call(this, 'div', { 15663 append: this.contentEl_, 15664 className: 'vjs-menu' 15665 }); 15666 15667 el.appendChild(this.contentEl_); 15668 15669 // Prevent clicks from bubbling up. Needed for Menu Buttons, 15670 // where a click on the parent is significant 15671 on(el, 'click', function (event) { 15672 event.preventDefault(); 15673 event.stopImmediatePropagation(); 15674 }); 15675 15676 return el; 15677 }; 15678 15679 Menu.prototype.dispose = function dispose() { 15680 this.contentEl_ = null; 15681 15682 _Component.prototype.dispose.call(this); 15683 }; 15684 15685 /** 15686 * Handle a `keydown` event on this menu. This listener is added in the constructor. 15687 * 15688 * @param {EventTarget~Event} event 15689 * A `keydown` event that happened on the menu. 15690 * 15691 * @listens keydown 15692 */ 15693 15694 15695 Menu.prototype.handleKeyPress = function handleKeyPress(event) { 15696 // Left and Down Arrows 15697 if (event.which === 37 || event.which === 40) { 15698 event.preventDefault(); 15699 this.stepForward(); 15700 15701 // Up and Right Arrows 15702 } else if (event.which === 38 || event.which === 39) { 15703 event.preventDefault(); 15704 this.stepBack(); 15705 } 15706 }; 15707 15708 /** 15709 * Move to next (lower) menu item for keyboard users. 15710 */ 15711 15712 15713 Menu.prototype.stepForward = function stepForward() { 15714 var stepChild = 0; 15715 15716 if (this.focusedChild_ !== undefined) { 15717 stepChild = this.focusedChild_ + 1; 15718 } 15719 this.focus(stepChild); 15720 }; 15721 15722 /** 15723 * Move to previous (higher) menu item for keyboard users. 15724 */ 15725 15726 15727 Menu.prototype.stepBack = function stepBack() { 15728 var stepChild = 0; 15729 15730 if (this.focusedChild_ !== undefined) { 15731 stepChild = this.focusedChild_ - 1; 15732 } 15733 this.focus(stepChild); 15734 }; 15735 15736 /** 15737 * Set focus on a {@link MenuItem} in the `Menu`. 15738 * 15739 * @param {Object|string} [item=0] 15740 * Index of child item set focus on. 15741 */ 15742 15743 15744 Menu.prototype.focus = function focus() { 15745 var item = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; 15746 15747 var children = this.children().slice(); 15748 var haveTitle = children.length && children[0].className && /vjs-menu-title/.test(children[0].className); 15749 15750 if (haveTitle) { 15751 children.shift(); 15752 } 15753 15754 if (children.length > 0) { 15755 if (item < 0) { 15756 item = 0; 15757 } else if (item >= children.length) { 15758 item = children.length - 1; 15759 } 15760 15761 this.focusedChild_ = item; 15762 15763 children[item].el_.focus(); 15764 } 15765 }; 15766 15767 return Menu; 15768}(Component); 15769 15770Component.registerComponent('Menu', Menu); 15771 15772/** 15773 * @file menu-button.js 15774 */ 15775/** 15776 * A `MenuButton` class for any popup {@link Menu}. 15777 * 15778 * @extends Component 15779 */ 15780 15781var MenuButton = function (_Component) { 15782 inherits(MenuButton, _Component); 15783 15784 /** 15785 * Creates an instance of this class. 15786 * 15787 * @param {Player} player 15788 * The `Player` that this class should be attached to. 15789 * 15790 * @param {Object} [options={}] 15791 * The key/value store of player options. 15792 */ 15793 function MenuButton(player) { 15794 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 15795 classCallCheck(this, MenuButton); 15796 15797 var _this = possibleConstructorReturn(this, _Component.call(this, player, options)); 15798 15799 _this.menuButton_ = new Button(player, options); 15800 15801 _this.menuButton_.controlText(_this.controlText_); 15802 _this.menuButton_.el_.setAttribute('aria-haspopup', 'true'); 15803 15804 // Add buildCSSClass values to the button, not the wrapper 15805 var buttonClass = Button.prototype.buildCSSClass(); 15806 15807 _this.menuButton_.el_.className = _this.buildCSSClass() + ' ' + buttonClass; 15808 _this.menuButton_.removeClass('vjs-control'); 15809 15810 _this.addChild(_this.menuButton_); 15811 15812 _this.update(); 15813 15814 _this.enabled_ = true; 15815 15816 _this.on(_this.menuButton_, 'tap', _this.handleClick); 15817 _this.on(_this.menuButton_, 'click', _this.handleClick); 15818 _this.on(_this.menuButton_, 'focus', _this.handleFocus); 15819 _this.on(_this.menuButton_, 'blur', _this.handleBlur); 15820 15821 _this.on('keydown', _this.handleSubmenuKeyPress); 15822 return _this; 15823 } 15824 15825 /** 15826 * Update the menu based on the current state of its items. 15827 */ 15828 15829 15830 MenuButton.prototype.update = function update() { 15831 var menu = this.createMenu(); 15832 15833 if (this.menu) { 15834 this.menu.dispose(); 15835 this.removeChild(this.menu); 15836 } 15837 15838 this.menu = menu; 15839 this.addChild(menu); 15840 15841 /** 15842 * Track the state of the menu button 15843 * 15844 * @type {Boolean} 15845 * @private 15846 */ 15847 this.buttonPressed_ = false; 15848 this.menuButton_.el_.setAttribute('aria-expanded', 'false'); 15849 15850 if (this.items && this.items.length <= this.hideThreshold_) { 15851 this.hide(); 15852 } else { 15853 this.show(); 15854 } 15855 }; 15856 15857 /** 15858 * Create the menu and add all items to it. 15859 * 15860 * @return {Menu} 15861 * The constructed menu 15862 */ 15863 15864 15865 MenuButton.prototype.createMenu = function createMenu() { 15866 var menu = new Menu(this.player_, { menuButton: this }); 15867 15868 /** 15869 * Hide the menu if the number of items is less than or equal to this threshold. This defaults 15870 * to 0 and whenever we add items which can be hidden to the menu we'll increment it. We list 15871 * it here because every time we run `createMenu` we need to reset the value. 15872 * 15873 * @protected 15874 * @type {Number} 15875 */ 15876 this.hideThreshold_ = 0; 15877 15878 // Add a title list item to the top 15879 if (this.options_.title) { 15880 var title = createEl('li', { 15881 className: 'vjs-menu-title', 15882 innerHTML: toTitleCase(this.options_.title), 15883 tabIndex: -1 15884 }); 15885 15886 this.hideThreshold_ += 1; 15887 15888 menu.children_.unshift(title); 15889 prependTo(title, menu.contentEl()); 15890 } 15891 15892 this.items = this.createItems(); 15893 15894 if (this.items) { 15895 // Add menu items to the menu 15896 for (var i = 0; i < this.items.length; i++) { 15897 menu.addItem(this.items[i]); 15898 } 15899 } 15900 15901 return menu; 15902 }; 15903 15904 /** 15905 * Create the list of menu items. Specific to each subclass. 15906 * 15907 * @abstract 15908 */ 15909 15910 15911 MenuButton.prototype.createItems = function createItems() {}; 15912 15913 /** 15914 * Create the `MenuButtons`s DOM element. 15915 * 15916 * @return {Element} 15917 * The element that gets created. 15918 */ 15919 15920 15921 MenuButton.prototype.createEl = function createEl$$1() { 15922 return _Component.prototype.createEl.call(this, 'div', { 15923 className: this.buildWrapperCSSClass() 15924 }, {}); 15925 }; 15926 15927 /** 15928 * Allow sub components to stack CSS class names for the wrapper element 15929 * 15930 * @return {string} 15931 * The constructed wrapper DOM `className` 15932 */ 15933 15934 15935 MenuButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() { 15936 var menuButtonClass = 'vjs-menu-button'; 15937 15938 // If the inline option is passed, we want to use different styles altogether. 15939 if (this.options_.inline === true) { 15940 menuButtonClass += '-inline'; 15941 } else { 15942 menuButtonClass += '-popup'; 15943 } 15944 15945 // TODO: Fix the CSS so that this isn't necessary 15946 var buttonClass = Button.prototype.buildCSSClass(); 15947 15948 return 'vjs-menu-button ' + menuButtonClass + ' ' + buttonClass + ' ' + _Component.prototype.buildCSSClass.call(this); 15949 }; 15950 15951 /** 15952 * Builds the default DOM `className`. 15953 * 15954 * @return {string} 15955 * The DOM `className` for this object. 15956 */ 15957 15958 15959 MenuButton.prototype.buildCSSClass = function buildCSSClass() { 15960 var menuButtonClass = 'vjs-menu-button'; 15961 15962 // If the inline option is passed, we want to use different styles altogether. 15963 if (this.options_.inline === true) { 15964 menuButtonClass += '-inline'; 15965 } else { 15966 menuButtonClass += '-popup'; 15967 } 15968 15969 return 'vjs-menu-button ' + menuButtonClass + ' ' + _Component.prototype.buildCSSClass.call(this); 15970 }; 15971 15972 /** 15973 * Get or set the localized control text that will be used for accessibility. 15974 * 15975 * > NOTE: This will come from the internal `menuButton_` element. 15976 * 15977 * @param {string} [text] 15978 * Control text for element. 15979 * 15980 * @param {Element} [el=this.menuButton_.el()] 15981 * Element to set the title on. 15982 * 15983 * @return {string} 15984 * - The control text when getting 15985 */ 15986 15987 15988 MenuButton.prototype.controlText = function controlText(text) { 15989 var el = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.menuButton_.el(); 15990 15991 return this.menuButton_.controlText(text, el); 15992 }; 15993 15994 /** 15995 * Handle a click on a `MenuButton`. 15996 * See {@link ClickableComponent#handleClick} for instances where this is called. 15997 * 15998 * @param {EventTarget~Event} event 15999 * The `keydown`, `tap`, or `click` event that caused this function to be 16000 * called. 16001 * 16002 * @listens tap 16003 * @listens click 16004 */ 16005 16006 16007 MenuButton.prototype.handleClick = function handleClick(event) { 16008 // When you click the button it adds focus, which will show the menu. 16009 // So we'll remove focus when the mouse leaves the button. Focus is needed 16010 // for tab navigation. 16011 16012 this.one(this.menu.contentEl(), 'mouseleave', bind(this, function (e) { 16013 this.unpressButton(); 16014 this.el_.blur(); 16015 })); 16016 if (this.buttonPressed_) { 16017 this.unpressButton(); 16018 } else { 16019 this.pressButton(); 16020 } 16021 }; 16022 16023 /** 16024 * Set the focus to the actual button, not to this element 16025 */ 16026 16027 16028 MenuButton.prototype.focus = function focus() { 16029 this.menuButton_.focus(); 16030 }; 16031 16032 /** 16033 * Remove the focus from the actual button, not this element 16034 */ 16035 16036 16037 MenuButton.prototype.blur = function blur() { 16038 this.menuButton_.blur(); 16039 }; 16040 16041 /** 16042 * This gets called when a `MenuButton` gains focus via a `focus` event. 16043 * Turns on listening for `keydown` events. When they happen it 16044 * calls `this.handleKeyPress`. 16045 * 16046 * @param {EventTarget~Event} event 16047 * The `focus` event that caused this function to be called. 16048 * 16049 * @listens focus 16050 */ 16051 16052 16053 MenuButton.prototype.handleFocus = function handleFocus() { 16054 on(document_1, 'keydown', bind(this, this.handleKeyPress)); 16055 }; 16056 16057 /** 16058 * Called when a `MenuButton` loses focus. Turns off the listener for 16059 * `keydown` events. Which Stops `this.handleKeyPress` from getting called. 16060 * 16061 * @param {EventTarget~Event} event 16062 * The `blur` event that caused this function to be called. 16063 * 16064 * @listens blur 16065 */ 16066 16067 16068 MenuButton.prototype.handleBlur = function handleBlur() { 16069 off(document_1, 'keydown', bind(this, this.handleKeyPress)); 16070 }; 16071 16072 /** 16073 * Handle tab, escape, down arrow, and up arrow keys for `MenuButton`. See 16074 * {@link ClickableComponent#handleKeyPress} for instances where this is called. 16075 * 16076 * @param {EventTarget~Event} event 16077 * The `keydown` event that caused this function to be called. 16078 * 16079 * @listens keydown 16080 */ 16081 16082 16083 MenuButton.prototype.handleKeyPress = function handleKeyPress(event) { 16084 16085 // Escape (27) key or Tab (9) key unpress the 'button' 16086 if (event.which === 27 || event.which === 9) { 16087 if (this.buttonPressed_) { 16088 this.unpressButton(); 16089 } 16090 // Don't preventDefault for Tab key - we still want to lose focus 16091 if (event.which !== 9) { 16092 event.preventDefault(); 16093 // Set focus back to the menu button's button 16094 this.menuButton_.el_.focus(); 16095 } 16096 // Up (38) key or Down (40) key press the 'button' 16097 } else if (event.which === 38 || event.which === 40) { 16098 if (!this.buttonPressed_) { 16099 this.pressButton(); 16100 event.preventDefault(); 16101 } 16102 } 16103 }; 16104 16105 /** 16106 * Handle a `keydown` event on a sub-menu. The listener for this is added in 16107 * the constructor. 16108 * 16109 * @param {EventTarget~Event} event 16110 * Key press event 16111 * 16112 * @listens keydown 16113 */ 16114 16115 16116 MenuButton.prototype.handleSubmenuKeyPress = function handleSubmenuKeyPress(event) { 16117 16118 // Escape (27) key or Tab (9) key unpress the 'button' 16119 if (event.which === 27 || event.which === 9) { 16120 if (this.buttonPressed_) { 16121 this.unpressButton(); 16122 } 16123 // Don't preventDefault for Tab key - we still want to lose focus 16124 if (event.which !== 9) { 16125 event.preventDefault(); 16126 // Set focus back to the menu button's button 16127 this.menuButton_.el_.focus(); 16128 } 16129 } 16130 }; 16131 16132 /** 16133 * Put the current `MenuButton` into a pressed state. 16134 */ 16135 16136 16137 MenuButton.prototype.pressButton = function pressButton() { 16138 if (this.enabled_) { 16139 this.buttonPressed_ = true; 16140 this.menu.lockShowing(); 16141 this.menuButton_.el_.setAttribute('aria-expanded', 'true'); 16142 16143 // set the focus into the submenu, except on iOS where it is resulting in 16144 // undesired scrolling behavior when the player is in an iframe 16145 if (IS_IOS && isInFrame()) { 16146 // Return early so that the menu isn't focused 16147 return; 16148 } 16149 16150 this.menu.focus(); 16151 } 16152 }; 16153 16154 /** 16155 * Take the current `MenuButton` out of a pressed state. 16156 */ 16157 16158 16159 MenuButton.prototype.unpressButton = function unpressButton() { 16160 if (this.enabled_) { 16161 this.buttonPressed_ = false; 16162 this.menu.unlockShowing(); 16163 this.menuButton_.el_.setAttribute('aria-expanded', 'false'); 16164 } 16165 }; 16166 16167 /** 16168 * Disable the `MenuButton`. Don't allow it to be clicked. 16169 */ 16170 16171 16172 MenuButton.prototype.disable = function disable() { 16173 this.unpressButton(); 16174 16175 this.enabled_ = false; 16176 this.addClass('vjs-disabled'); 16177 16178 this.menuButton_.disable(); 16179 }; 16180 16181 /** 16182 * Enable the `MenuButton`. Allow it to be clicked. 16183 */ 16184 16185 16186 MenuButton.prototype.enable = function enable() { 16187 this.enabled_ = true; 16188 this.removeClass('vjs-disabled'); 16189 16190 this.menuButton_.enable(); 16191 }; 16192 16193 return MenuButton; 16194}(Component); 16195 16196Component.registerComponent('MenuButton', MenuButton); 16197 16198/** 16199 * @file track-button.js 16200 */ 16201/** 16202 * The base class for buttons that toggle specific track types (e.g. subtitles). 16203 * 16204 * @extends MenuButton 16205 */ 16206 16207var TrackButton = function (_MenuButton) { 16208 inherits(TrackButton, _MenuButton); 16209 16210 /** 16211 * Creates an instance of this class. 16212 * 16213 * @param {Player} player 16214 * The `Player` that this class should be attached to. 16215 * 16216 * @param {Object} [options] 16217 * The key/value store of player options. 16218 */ 16219 function TrackButton(player, options) { 16220 classCallCheck(this, TrackButton); 16221 16222 var tracks = options.tracks; 16223 16224 var _this = possibleConstructorReturn(this, _MenuButton.call(this, player, options)); 16225 16226 if (_this.items.length <= 1) { 16227 _this.hide(); 16228 } 16229 16230 if (!tracks) { 16231 return possibleConstructorReturn(_this); 16232 } 16233 16234 var updateHandler = bind(_this, _this.update); 16235 16236 tracks.addEventListener('removetrack', updateHandler); 16237 tracks.addEventListener('addtrack', updateHandler); 16238 _this.player_.on('ready', updateHandler); 16239 16240 _this.player_.on('dispose', function () { 16241 tracks.removeEventListener('removetrack', updateHandler); 16242 tracks.removeEventListener('addtrack', updateHandler); 16243 }); 16244 return _this; 16245 } 16246 16247 return TrackButton; 16248}(MenuButton); 16249 16250Component.registerComponent('TrackButton', TrackButton); 16251 16252/** 16253 * @file menu-item.js 16254 */ 16255/** 16256 * The component for a menu item. `<li>` 16257 * 16258 * @extends ClickableComponent 16259 */ 16260 16261var MenuItem = function (_ClickableComponent) { 16262 inherits(MenuItem, _ClickableComponent); 16263 16264 /** 16265 * Creates an instance of the this class. 16266 * 16267 * @param {Player} player 16268 * The `Player` that this class should be attached to. 16269 * 16270 * @param {Object} [options={}] 16271 * The key/value store of player options. 16272 * 16273 */ 16274 function MenuItem(player, options) { 16275 classCallCheck(this, MenuItem); 16276 16277 var _this = possibleConstructorReturn(this, _ClickableComponent.call(this, player, options)); 16278 16279 _this.selectable = options.selectable; 16280 16281 _this.selected(options.selected); 16282 16283 if (_this.selectable) { 16284 // TODO: May need to be either menuitemcheckbox or menuitemradio, 16285 // and may need logical grouping of menu items. 16286 _this.el_.setAttribute('role', 'menuitemcheckbox'); 16287 } else { 16288 _this.el_.setAttribute('role', 'menuitem'); 16289 } 16290 return _this; 16291 } 16292 16293 /** 16294 * Create the `MenuItem's DOM element 16295 * 16296 * @param {string} [type=li] 16297 * Element's node type, not actually used, always set to `li`. 16298 * 16299 * @param {Object} [props={}] 16300 * An object of properties that should be set on the element 16301 * 16302 * @param {Object} [attrs={}] 16303 * An object of attributes that should be set on the element 16304 * 16305 * @return {Element} 16306 * The element that gets created. 16307 */ 16308 16309 16310 MenuItem.prototype.createEl = function createEl(type, props, attrs) { 16311 // The control is textual, not just an icon 16312 this.nonIconControl = true; 16313 16314 return _ClickableComponent.prototype.createEl.call(this, 'li', assign({ 16315 className: 'vjs-menu-item', 16316 innerHTML: '<span class="vjs-menu-item-text">' + this.localize(this.options_.label) + '</span>', 16317 tabIndex: -1 16318 }, props), attrs); 16319 }; 16320 16321 /** 16322 * Any click on a `MenuItem` puts int into the selected state. 16323 * See {@link ClickableComponent#handleClick} for instances where this is called. 16324 * 16325 * @param {EventTarget~Event} event 16326 * The `keydown`, `tap`, or `click` event that caused this function to be 16327 * called. 16328 * 16329 * @listens tap 16330 * @listens click 16331 */ 16332 16333 16334 MenuItem.prototype.handleClick = function handleClick(event) { 16335 this.selected(true); 16336 }; 16337 16338 /** 16339 * Set the state for this menu item as selected or not. 16340 * 16341 * @param {boolean} selected 16342 * if the menu item is selected or not 16343 */ 16344 16345 16346 MenuItem.prototype.selected = function selected(_selected) { 16347 if (this.selectable) { 16348 if (_selected) { 16349 this.addClass('vjs-selected'); 16350 this.el_.setAttribute('aria-checked', 'true'); 16351 // aria-checked isn't fully supported by browsers/screen readers, 16352 // so indicate selected state to screen reader in the control text. 16353 this.controlText(', selected'); 16354 } else { 16355 this.removeClass('vjs-selected'); 16356 this.el_.setAttribute('aria-checked', 'false'); 16357 // Indicate un-selected state to screen reader 16358 this.controlText(''); 16359 } 16360 } 16361 }; 16362 16363 return MenuItem; 16364}(ClickableComponent); 16365 16366Component.registerComponent('MenuItem', MenuItem); 16367 16368/** 16369 * @file text-track-menu-item.js 16370 */ 16371/** 16372 * The specific menu item type for selecting a language within a text track kind 16373 * 16374 * @extends MenuItem 16375 */ 16376 16377var TextTrackMenuItem = function (_MenuItem) { 16378 inherits(TextTrackMenuItem, _MenuItem); 16379 16380 /** 16381 * Creates an instance of this class. 16382 * 16383 * @param {Player} player 16384 * The `Player` that this class should be attached to. 16385 * 16386 * @param {Object} [options] 16387 * The key/value store of player options. 16388 */ 16389 function TextTrackMenuItem(player, options) { 16390 classCallCheck(this, TextTrackMenuItem); 16391 16392 var track = options.track; 16393 var tracks = player.textTracks(); 16394 16395 // Modify options for parent MenuItem class's init. 16396 options.label = track.label || track.language || 'Unknown'; 16397 options.selected = track.mode === 'showing'; 16398 16399 var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options)); 16400 16401 _this.track = track; 16402 var changeHandler = function changeHandler() { 16403 for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { 16404 args[_key] = arguments[_key]; 16405 } 16406 16407 _this.handleTracksChange.apply(_this, args); 16408 }; 16409 var selectedLanguageChangeHandler = function selectedLanguageChangeHandler() { 16410 for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { 16411 args[_key2] = arguments[_key2]; 16412 } 16413 16414 _this.handleSelectedLanguageChange.apply(_this, args); 16415 }; 16416 16417 player.on(['loadstart', 'texttrackchange'], changeHandler); 16418 tracks.addEventListener('change', changeHandler); 16419 tracks.addEventListener('selectedlanguagechange', selectedLanguageChangeHandler); 16420 _this.on('dispose', function () { 16421 player.off(['loadstart', 'texttrackchange'], changeHandler); 16422 tracks.removeEventListener('change', changeHandler); 16423 tracks.removeEventListener('selectedlanguagechange', selectedLanguageChangeHandler); 16424 }); 16425 16426 // iOS7 doesn't dispatch change events to TextTrackLists when an 16427 // associated track's mode changes. Without something like 16428 // Object.observe() (also not present on iOS7), it's not 16429 // possible to detect changes to the mode attribute and polyfill 16430 // the change event. As a poor substitute, we manually dispatch 16431 // change events whenever the controls modify the mode. 16432 if (tracks.onchange === undefined) { 16433 var event = void 0; 16434 16435 _this.on(['tap', 'click'], function () { 16436 if (_typeof(window_1.Event) !== 'object') { 16437 // Android 2.3 throws an Illegal Constructor error for window.Event 16438 try { 16439 event = new window_1.Event('change'); 16440 } catch (err) { 16441 // continue regardless of error 16442 } 16443 } 16444 16445 if (!event) { 16446 event = document_1.createEvent('Event'); 16447 event.initEvent('change', true, true); 16448 } 16449 16450 tracks.dispatchEvent(event); 16451 }); 16452 } 16453 16454 // set the default state based on current tracks 16455 _this.handleTracksChange(); 16456 return _this; 16457 } 16458 16459 /** 16460 * This gets called when an `TextTrackMenuItem` is "clicked". See 16461 * {@link ClickableComponent} for more detailed information on what a click can be. 16462 * 16463 * @param {EventTarget~Event} event 16464 * The `keydown`, `tap`, or `click` event that caused this function to be 16465 * called. 16466 * 16467 * @listens tap 16468 * @listens click 16469 */ 16470 16471 16472 TextTrackMenuItem.prototype.handleClick = function handleClick(event) { 16473 var kind = this.track.kind; 16474 var kinds = this.track.kinds; 16475 var tracks = this.player_.textTracks(); 16476 16477 if (!kinds) { 16478 kinds = [kind]; 16479 } 16480 16481 _MenuItem.prototype.handleClick.call(this, event); 16482 16483 if (!tracks) { 16484 return; 16485 } 16486 16487 for (var i = 0; i < tracks.length; i++) { 16488 var track = tracks[i]; 16489 16490 if (track === this.track && kinds.indexOf(track.kind) > -1) { 16491 if (track.mode !== 'showing') { 16492 track.mode = 'showing'; 16493 } 16494 } else if (track.mode !== 'disabled') { 16495 track.mode = 'disabled'; 16496 } 16497 } 16498 }; 16499 16500 /** 16501 * Handle text track list change 16502 * 16503 * @param {EventTarget~Event} event 16504 * The `change` event that caused this function to be called. 16505 * 16506 * @listens TextTrackList#change 16507 */ 16508 16509 16510 TextTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) { 16511 this.selected(this.track.mode === 'showing'); 16512 }; 16513 16514 TextTrackMenuItem.prototype.handleSelectedLanguageChange = function handleSelectedLanguageChange(event) { 16515 if (this.track.mode === 'showing') { 16516 var selectedLanguage = this.player_.cache_.selectedLanguage; 16517 16518 // Don't replace the kind of track across the same language 16519 if (selectedLanguage && selectedLanguage.enabled && selectedLanguage.language === this.track.language && selectedLanguage.kind !== this.track.kind) { 16520 return; 16521 } 16522 16523 this.player_.cache_.selectedLanguage = { 16524 enabled: true, 16525 language: this.track.language, 16526 kind: this.track.kind 16527 }; 16528 } 16529 }; 16530 16531 TextTrackMenuItem.prototype.dispose = function dispose() { 16532 // remove reference to track object on dispose 16533 this.track = null; 16534 16535 _MenuItem.prototype.dispose.call(this); 16536 }; 16537 16538 return TextTrackMenuItem; 16539}(MenuItem); 16540 16541Component.registerComponent('TextTrackMenuItem', TextTrackMenuItem); 16542 16543/** 16544 * @file off-text-track-menu-item.js 16545 */ 16546/** 16547 * A special menu item for turning of a specific type of text track 16548 * 16549 * @extends TextTrackMenuItem 16550 */ 16551 16552var OffTextTrackMenuItem = function (_TextTrackMenuItem) { 16553 inherits(OffTextTrackMenuItem, _TextTrackMenuItem); 16554 16555 /** 16556 * Creates an instance of this class. 16557 * 16558 * @param {Player} player 16559 * The `Player` that this class should be attached to. 16560 * 16561 * @param {Object} [options] 16562 * The key/value store of player options. 16563 */ 16564 function OffTextTrackMenuItem(player, options) { 16565 classCallCheck(this, OffTextTrackMenuItem); 16566 16567 // Create pseudo track info 16568 // Requires options['kind'] 16569 options.track = { 16570 player: player, 16571 kind: options.kind, 16572 kinds: options.kinds, 16573 'default': false, 16574 mode: 'disabled' 16575 }; 16576 16577 if (!options.kinds) { 16578 options.kinds = [options.kind]; 16579 } 16580 16581 if (options.label) { 16582 options.track.label = options.label; 16583 } else { 16584 options.track.label = options.kinds.join(' and ') + ' off'; 16585 } 16586 16587 // MenuItem is selectable 16588 options.selectable = true; 16589 16590 return possibleConstructorReturn(this, _TextTrackMenuItem.call(this, player, options)); 16591 } 16592 16593 /** 16594 * Handle text track change 16595 * 16596 * @param {EventTarget~Event} event 16597 * The event that caused this function to run 16598 */ 16599 16600 16601 OffTextTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) { 16602 var tracks = this.player().textTracks(); 16603 var selected = true; 16604 16605 for (var i = 0, l = tracks.length; i < l; i++) { 16606 var track = tracks[i]; 16607 16608 if (this.options_.kinds.indexOf(track.kind) > -1 && track.mode === 'showing') { 16609 selected = false; 16610 break; 16611 } 16612 } 16613 16614 this.selected(selected); 16615 }; 16616 16617 OffTextTrackMenuItem.prototype.handleSelectedLanguageChange = function handleSelectedLanguageChange(event) { 16618 var tracks = this.player().textTracks(); 16619 var allHidden = true; 16620 16621 for (var i = 0, l = tracks.length; i < l; i++) { 16622 var track = tracks[i]; 16623 16624 if (['captions', 'descriptions', 'subtitles'].indexOf(track.kind) > -1 && track.mode === 'showing') { 16625 allHidden = false; 16626 break; 16627 } 16628 } 16629 16630 if (allHidden) { 16631 this.player_.cache_.selectedLanguage = { 16632 enabled: false 16633 }; 16634 } 16635 }; 16636 16637 return OffTextTrackMenuItem; 16638}(TextTrackMenuItem); 16639 16640Component.registerComponent('OffTextTrackMenuItem', OffTextTrackMenuItem); 16641 16642/** 16643 * @file text-track-button.js 16644 */ 16645/** 16646 * The base class for buttons that toggle specific text track types (e.g. subtitles) 16647 * 16648 * @extends MenuButton 16649 */ 16650 16651var TextTrackButton = function (_TrackButton) { 16652 inherits(TextTrackButton, _TrackButton); 16653 16654 /** 16655 * Creates an instance of this class. 16656 * 16657 * @param {Player} player 16658 * The `Player` that this class should be attached to. 16659 * 16660 * @param {Object} [options={}] 16661 * The key/value store of player options. 16662 */ 16663 function TextTrackButton(player) { 16664 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 16665 classCallCheck(this, TextTrackButton); 16666 16667 options.tracks = player.textTracks(); 16668 16669 return possibleConstructorReturn(this, _TrackButton.call(this, player, options)); 16670 } 16671 16672 /** 16673 * Create a menu item for each text track 16674 * 16675 * @param {TextTrackMenuItem[]} [items=[]] 16676 * Existing array of items to use during creation 16677 * 16678 * @return {TextTrackMenuItem[]} 16679 * Array of menu items that were created 16680 */ 16681 16682 16683 TextTrackButton.prototype.createItems = function createItems() { 16684 var items = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; 16685 var TrackMenuItem = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : TextTrackMenuItem; 16686 16687 16688 // Label is an overide for the [track] off label 16689 // USed to localise captions/subtitles 16690 var label = void 0; 16691 16692 if (this.label_) { 16693 label = this.label_ + ' off'; 16694 } 16695 // Add an OFF menu item to turn all tracks off 16696 items.push(new OffTextTrackMenuItem(this.player_, { 16697 kinds: this.kinds_, 16698 kind: this.kind_, 16699 label: label 16700 })); 16701 16702 this.hideThreshold_ += 1; 16703 16704 var tracks = this.player_.textTracks(); 16705 16706 if (!Array.isArray(this.kinds_)) { 16707 this.kinds_ = [this.kind_]; 16708 } 16709 16710 for (var i = 0; i < tracks.length; i++) { 16711 var track = tracks[i]; 16712 16713 // only add tracks that are of an appropriate kind and have a label 16714 if (this.kinds_.indexOf(track.kind) > -1) { 16715 16716 var item = new TrackMenuItem(this.player_, { 16717 track: track, 16718 // MenuItem is selectable 16719 selectable: true 16720 }); 16721 16722 item.addClass('vjs-' + track.kind + '-menu-item'); 16723 items.push(item); 16724 } 16725 } 16726 16727 return items; 16728 }; 16729 16730 return TextTrackButton; 16731}(TrackButton); 16732 16733Component.registerComponent('TextTrackButton', TextTrackButton); 16734 16735/** 16736 * @file chapters-track-menu-item.js 16737 */ 16738/** 16739 * The chapter track menu item 16740 * 16741 * @extends MenuItem 16742 */ 16743 16744var ChaptersTrackMenuItem = function (_MenuItem) { 16745 inherits(ChaptersTrackMenuItem, _MenuItem); 16746 16747 /** 16748 * Creates an instance of this class. 16749 * 16750 * @param {Player} player 16751 * The `Player` that this class should be attached to. 16752 * 16753 * @param {Object} [options] 16754 * The key/value store of player options. 16755 */ 16756 function ChaptersTrackMenuItem(player, options) { 16757 classCallCheck(this, ChaptersTrackMenuItem); 16758 16759 var track = options.track; 16760 var cue = options.cue; 16761 var currentTime = player.currentTime(); 16762 16763 // Modify options for parent MenuItem class's init. 16764 options.selectable = true; 16765 options.label = cue.text; 16766 options.selected = cue.startTime <= currentTime && currentTime < cue.endTime; 16767 16768 var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options)); 16769 16770 _this.track = track; 16771 _this.cue = cue; 16772 track.addEventListener('cuechange', bind(_this, _this.update)); 16773 return _this; 16774 } 16775 16776 /** 16777 * This gets called when an `ChaptersTrackMenuItem` is "clicked". See 16778 * {@link ClickableComponent} for more detailed information on what a click can be. 16779 * 16780 * @param {EventTarget~Event} [event] 16781 * The `keydown`, `tap`, or `click` event that caused this function to be 16782 * called. 16783 * 16784 * @listens tap 16785 * @listens click 16786 */ 16787 16788 16789 ChaptersTrackMenuItem.prototype.handleClick = function handleClick(event) { 16790 _MenuItem.prototype.handleClick.call(this); 16791 this.player_.currentTime(this.cue.startTime); 16792 this.update(this.cue.startTime); 16793 }; 16794 16795 /** 16796 * Update chapter menu item 16797 * 16798 * @param {EventTarget~Event} [event] 16799 * The `cuechange` event that caused this function to run. 16800 * 16801 * @listens TextTrack#cuechange 16802 */ 16803 16804 16805 ChaptersTrackMenuItem.prototype.update = function update(event) { 16806 var cue = this.cue; 16807 var currentTime = this.player_.currentTime(); 16808 16809 // vjs.log(currentTime, cue.startTime); 16810 this.selected(cue.startTime <= currentTime && currentTime < cue.endTime); 16811 }; 16812 16813 return ChaptersTrackMenuItem; 16814}(MenuItem); 16815 16816Component.registerComponent('ChaptersTrackMenuItem', ChaptersTrackMenuItem); 16817 16818/** 16819 * @file chapters-button.js 16820 */ 16821/** 16822 * The button component for toggling and selecting chapters 16823 * Chapters act much differently than other text tracks 16824 * Cues are navigation vs. other tracks of alternative languages 16825 * 16826 * @extends TextTrackButton 16827 */ 16828 16829var ChaptersButton = function (_TextTrackButton) { 16830 inherits(ChaptersButton, _TextTrackButton); 16831 16832 /** 16833 * Creates an instance of this class. 16834 * 16835 * @param {Player} player 16836 * The `Player` that this class should be attached to. 16837 * 16838 * @param {Object} [options] 16839 * The key/value store of player options. 16840 * 16841 * @param {Component~ReadyCallback} [ready] 16842 * The function to call when this function is ready. 16843 */ 16844 function ChaptersButton(player, options, ready) { 16845 classCallCheck(this, ChaptersButton); 16846 return possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready)); 16847 } 16848 16849 /** 16850 * Builds the default DOM `className`. 16851 * 16852 * @return {string} 16853 * The DOM `className` for this object. 16854 */ 16855 16856 16857 ChaptersButton.prototype.buildCSSClass = function buildCSSClass() { 16858 return 'vjs-chapters-button ' + _TextTrackButton.prototype.buildCSSClass.call(this); 16859 }; 16860 16861 ChaptersButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() { 16862 return 'vjs-chapters-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this); 16863 }; 16864 16865 /** 16866 * Update the menu based on the current state of its items. 16867 * 16868 * @param {EventTarget~Event} [event] 16869 * An event that triggered this function to run. 16870 * 16871 * @listens TextTrackList#addtrack 16872 * @listens TextTrackList#removetrack 16873 * @listens TextTrackList#change 16874 */ 16875 16876 16877 ChaptersButton.prototype.update = function update(event) { 16878 if (!this.track_ || event && (event.type === 'addtrack' || event.type === 'removetrack')) { 16879 this.setTrack(this.findChaptersTrack()); 16880 } 16881 _TextTrackButton.prototype.update.call(this); 16882 }; 16883 16884 /** 16885 * Set the currently selected track for the chapters button. 16886 * 16887 * @param {TextTrack} track 16888 * The new track to select. Nothing will change if this is the currently selected 16889 * track. 16890 */ 16891 16892 16893 ChaptersButton.prototype.setTrack = function setTrack(track) { 16894 if (this.track_ === track) { 16895 return; 16896 } 16897 16898 if (!this.updateHandler_) { 16899 this.updateHandler_ = this.update.bind(this); 16900 } 16901 16902 // here this.track_ refers to the old track instance 16903 if (this.track_) { 16904 var remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_); 16905 16906 if (remoteTextTrackEl) { 16907 remoteTextTrackEl.removeEventListener('load', this.updateHandler_); 16908 } 16909 16910 this.track_ = null; 16911 } 16912 16913 this.track_ = track; 16914 16915 // here this.track_ refers to the new track instance 16916 if (this.track_) { 16917 this.track_.mode = 'hidden'; 16918 16919 var _remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_); 16920 16921 if (_remoteTextTrackEl) { 16922 _remoteTextTrackEl.addEventListener('load', this.updateHandler_); 16923 } 16924 } 16925 }; 16926 16927 /** 16928 * Find the track object that is currently in use by this ChaptersButton 16929 * 16930 * @return {TextTrack|undefined} 16931 * The current track or undefined if none was found. 16932 */ 16933 16934 16935 ChaptersButton.prototype.findChaptersTrack = function findChaptersTrack() { 16936 var tracks = this.player_.textTracks() || []; 16937 16938 for (var i = tracks.length - 1; i >= 0; i--) { 16939 // We will always choose the last track as our chaptersTrack 16940 var track = tracks[i]; 16941 16942 if (track.kind === this.kind_) { 16943 return track; 16944 } 16945 } 16946 }; 16947 16948 /** 16949 * Get the caption for the ChaptersButton based on the track label. This will also 16950 * use the current tracks localized kind as a fallback if a label does not exist. 16951 * 16952 * @return {string} 16953 * The tracks current label or the localized track kind. 16954 */ 16955 16956 16957 ChaptersButton.prototype.getMenuCaption = function getMenuCaption() { 16958 if (this.track_ && this.track_.label) { 16959 return this.track_.label; 16960 } 16961 return this.localize(toTitleCase(this.kind_)); 16962 }; 16963 16964 /** 16965 * Create menu from chapter track 16966 * 16967 * @return {Menu} 16968 * New menu for the chapter buttons 16969 */ 16970 16971 16972 ChaptersButton.prototype.createMenu = function createMenu() { 16973 this.options_.title = this.getMenuCaption(); 16974 return _TextTrackButton.prototype.createMenu.call(this); 16975 }; 16976 16977 /** 16978 * Create a menu item for each text track 16979 * 16980 * @return {TextTrackMenuItem[]} 16981 * Array of menu items 16982 */ 16983 16984 16985 ChaptersButton.prototype.createItems = function createItems() { 16986 var items = []; 16987 16988 if (!this.track_) { 16989 return items; 16990 } 16991 16992 var cues = this.track_.cues; 16993 16994 if (!cues) { 16995 return items; 16996 } 16997 16998 for (var i = 0, l = cues.length; i < l; i++) { 16999 var cue = cues[i]; 17000 var mi = new ChaptersTrackMenuItem(this.player_, { track: this.track_, cue: cue }); 17001 17002 items.push(mi); 17003 } 17004 17005 return items; 17006 }; 17007 17008 return ChaptersButton; 17009}(TextTrackButton); 17010 17011/** 17012 * `kind` of TextTrack to look for to associate it with this menu. 17013 * 17014 * @type {string} 17015 * @private 17016 */ 17017 17018 17019ChaptersButton.prototype.kind_ = 'chapters'; 17020 17021/** 17022 * The text that should display over the `ChaptersButton`s controls. Added for localization. 17023 * 17024 * @type {string} 17025 * @private 17026 */ 17027ChaptersButton.prototype.controlText_ = 'Chapters'; 17028 17029Component.registerComponent('ChaptersButton', ChaptersButton); 17030 17031/** 17032 * @file descriptions-button.js 17033 */ 17034/** 17035 * The button component for toggling and selecting descriptions 17036 * 17037 * @extends TextTrackButton 17038 */ 17039 17040var DescriptionsButton = function (_TextTrackButton) { 17041 inherits(DescriptionsButton, _TextTrackButton); 17042 17043 /** 17044 * Creates an instance of this class. 17045 * 17046 * @param {Player} player 17047 * The `Player` that this class should be attached to. 17048 * 17049 * @param {Object} [options] 17050 * The key/value store of player options. 17051 * 17052 * @param {Component~ReadyCallback} [ready] 17053 * The function to call when this component is ready. 17054 */ 17055 function DescriptionsButton(player, options, ready) { 17056 classCallCheck(this, DescriptionsButton); 17057 17058 var _this = possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready)); 17059 17060 var tracks = player.textTracks(); 17061 var changeHandler = bind(_this, _this.handleTracksChange); 17062 17063 tracks.addEventListener('change', changeHandler); 17064 _this.on('dispose', function () { 17065 tracks.removeEventListener('change', changeHandler); 17066 }); 17067 return _this; 17068 } 17069 17070 /** 17071 * Handle text track change 17072 * 17073 * @param {EventTarget~Event} event 17074 * The event that caused this function to run 17075 * 17076 * @listens TextTrackList#change 17077 */ 17078 17079 17080 DescriptionsButton.prototype.handleTracksChange = function handleTracksChange(event) { 17081 var tracks = this.player().textTracks(); 17082 var disabled = false; 17083 17084 // Check whether a track of a different kind is showing 17085 for (var i = 0, l = tracks.length; i < l; i++) { 17086 var track = tracks[i]; 17087 17088 if (track.kind !== this.kind_ && track.mode === 'showing') { 17089 disabled = true; 17090 break; 17091 } 17092 } 17093 17094 // If another track is showing, disable this menu button 17095 if (disabled) { 17096 this.disable(); 17097 } else { 17098 this.enable(); 17099 } 17100 }; 17101 17102 /** 17103 * Builds the default DOM `className`. 17104 * 17105 * @return {string} 17106 * The DOM `className` for this object. 17107 */ 17108 17109 17110 DescriptionsButton.prototype.buildCSSClass = function buildCSSClass() { 17111 return 'vjs-descriptions-button ' + _TextTrackButton.prototype.buildCSSClass.call(this); 17112 }; 17113 17114 DescriptionsButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() { 17115 return 'vjs-descriptions-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this); 17116 }; 17117 17118 return DescriptionsButton; 17119}(TextTrackButton); 17120 17121/** 17122 * `kind` of TextTrack to look for to associate it with this menu. 17123 * 17124 * @type {string} 17125 * @private 17126 */ 17127 17128 17129DescriptionsButton.prototype.kind_ = 'descriptions'; 17130 17131/** 17132 * The text that should display over the `DescriptionsButton`s controls. Added for localization. 17133 * 17134 * @type {string} 17135 * @private 17136 */ 17137DescriptionsButton.prototype.controlText_ = 'Descriptions'; 17138 17139Component.registerComponent('DescriptionsButton', DescriptionsButton); 17140 17141/** 17142 * @file subtitles-button.js 17143 */ 17144/** 17145 * The button component for toggling and selecting subtitles 17146 * 17147 * @extends TextTrackButton 17148 */ 17149 17150var SubtitlesButton = function (_TextTrackButton) { 17151 inherits(SubtitlesButton, _TextTrackButton); 17152 17153 /** 17154 * Creates an instance of this class. 17155 * 17156 * @param {Player} player 17157 * The `Player` that this class should be attached to. 17158 * 17159 * @param {Object} [options] 17160 * The key/value store of player options. 17161 * 17162 * @param {Component~ReadyCallback} [ready] 17163 * The function to call when this component is ready. 17164 */ 17165 function SubtitlesButton(player, options, ready) { 17166 classCallCheck(this, SubtitlesButton); 17167 return possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready)); 17168 } 17169 17170 /** 17171 * Builds the default DOM `className`. 17172 * 17173 * @return {string} 17174 * The DOM `className` for this object. 17175 */ 17176 17177 17178 SubtitlesButton.prototype.buildCSSClass = function buildCSSClass() { 17179 return 'vjs-subtitles-button ' + _TextTrackButton.prototype.buildCSSClass.call(this); 17180 }; 17181 17182 SubtitlesButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() { 17183 return 'vjs-subtitles-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this); 17184 }; 17185 17186 return SubtitlesButton; 17187}(TextTrackButton); 17188 17189/** 17190 * `kind` of TextTrack to look for to associate it with this menu. 17191 * 17192 * @type {string} 17193 * @private 17194 */ 17195 17196 17197SubtitlesButton.prototype.kind_ = 'subtitles'; 17198 17199/** 17200 * The text that should display over the `SubtitlesButton`s controls. Added for localization. 17201 * 17202 * @type {string} 17203 * @private 17204 */ 17205SubtitlesButton.prototype.controlText_ = 'Subtitles'; 17206 17207Component.registerComponent('SubtitlesButton', SubtitlesButton); 17208 17209/** 17210 * @file caption-settings-menu-item.js 17211 */ 17212/** 17213 * The menu item for caption track settings menu 17214 * 17215 * @extends TextTrackMenuItem 17216 */ 17217 17218var CaptionSettingsMenuItem = function (_TextTrackMenuItem) { 17219 inherits(CaptionSettingsMenuItem, _TextTrackMenuItem); 17220 17221 /** 17222 * Creates an instance of this class. 17223 * 17224 * @param {Player} player 17225 * The `Player` that this class should be attached to. 17226 * 17227 * @param {Object} [options] 17228 * The key/value store of player options. 17229 */ 17230 function CaptionSettingsMenuItem(player, options) { 17231 classCallCheck(this, CaptionSettingsMenuItem); 17232 17233 options.track = { 17234 player: player, 17235 kind: options.kind, 17236 label: options.kind + ' settings', 17237 selectable: false, 17238 'default': false, 17239 mode: 'disabled' 17240 }; 17241 17242 // CaptionSettingsMenuItem has no concept of 'selected' 17243 options.selectable = false; 17244 17245 options.name = 'CaptionSettingsMenuItem'; 17246 17247 var _this = possibleConstructorReturn(this, _TextTrackMenuItem.call(this, player, options)); 17248 17249 _this.addClass('vjs-texttrack-settings'); 17250 _this.controlText(', opens ' + options.kind + ' settings dialog'); 17251 return _this; 17252 } 17253 17254 /** 17255 * This gets called when an `CaptionSettingsMenuItem` is "clicked". See 17256 * {@link ClickableComponent} for more detailed information on what a click can be. 17257 * 17258 * @param {EventTarget~Event} [event] 17259 * The `keydown`, `tap`, or `click` event that caused this function to be 17260 * called. 17261 * 17262 * @listens tap 17263 * @listens click 17264 */ 17265 17266 17267 CaptionSettingsMenuItem.prototype.handleClick = function handleClick(event) { 17268 this.player().getChild('textTrackSettings').open(); 17269 }; 17270 17271 return CaptionSettingsMenuItem; 17272}(TextTrackMenuItem); 17273 17274Component.registerComponent('CaptionSettingsMenuItem', CaptionSettingsMenuItem); 17275 17276/** 17277 * @file captions-button.js 17278 */ 17279/** 17280 * The button component for toggling and selecting captions 17281 * 17282 * @extends TextTrackButton 17283 */ 17284 17285var CaptionsButton = function (_TextTrackButton) { 17286 inherits(CaptionsButton, _TextTrackButton); 17287 17288 /** 17289 * Creates an instance of this class. 17290 * 17291 * @param {Player} player 17292 * The `Player` that this class should be attached to. 17293 * 17294 * @param {Object} [options] 17295 * The key/value store of player options. 17296 * 17297 * @param {Component~ReadyCallback} [ready] 17298 * The function to call when this component is ready. 17299 */ 17300 function CaptionsButton(player, options, ready) { 17301 classCallCheck(this, CaptionsButton); 17302 return possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready)); 17303 } 17304 17305 /** 17306 * Builds the default DOM `className`. 17307 * 17308 * @return {string} 17309 * The DOM `className` for this object. 17310 */ 17311 17312 17313 CaptionsButton.prototype.buildCSSClass = function buildCSSClass() { 17314 return 'vjs-captions-button ' + _TextTrackButton.prototype.buildCSSClass.call(this); 17315 }; 17316 17317 CaptionsButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() { 17318 return 'vjs-captions-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this); 17319 }; 17320 17321 /** 17322 * Create caption menu items 17323 * 17324 * @return {CaptionSettingsMenuItem[]} 17325 * The array of current menu items. 17326 */ 17327 17328 17329 CaptionsButton.prototype.createItems = function createItems() { 17330 var items = []; 17331 17332 if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks)) { 17333 items.push(new CaptionSettingsMenuItem(this.player_, { kind: this.kind_ })); 17334 17335 this.hideThreshold_ += 1; 17336 } 17337 17338 return _TextTrackButton.prototype.createItems.call(this, items); 17339 }; 17340 17341 return CaptionsButton; 17342}(TextTrackButton); 17343 17344/** 17345 * `kind` of TextTrack to look for to associate it with this menu. 17346 * 17347 * @type {string} 17348 * @private 17349 */ 17350 17351 17352CaptionsButton.prototype.kind_ = 'captions'; 17353 17354/** 17355 * The text that should display over the `CaptionsButton`s controls. Added for localization. 17356 * 17357 * @type {string} 17358 * @private 17359 */ 17360CaptionsButton.prototype.controlText_ = 'Captions'; 17361 17362Component.registerComponent('CaptionsButton', CaptionsButton); 17363 17364/** 17365 * @file subs-caps-menu-item.js 17366 */ 17367/** 17368 * SubsCapsMenuItem has an [cc] icon to distinguish captions from subtitles 17369 * in the SubsCapsMenu. 17370 * 17371 * @extends TextTrackMenuItem 17372 */ 17373 17374var SubsCapsMenuItem = function (_TextTrackMenuItem) { 17375 inherits(SubsCapsMenuItem, _TextTrackMenuItem); 17376 17377 function SubsCapsMenuItem() { 17378 classCallCheck(this, SubsCapsMenuItem); 17379 return possibleConstructorReturn(this, _TextTrackMenuItem.apply(this, arguments)); 17380 } 17381 17382 SubsCapsMenuItem.prototype.createEl = function createEl(type, props, attrs) { 17383 var innerHTML = '<span class="vjs-menu-item-text">' + this.localize(this.options_.label); 17384 17385 if (this.options_.track.kind === 'captions') { 17386 innerHTML += '\n <span aria-hidden="true" class="vjs-icon-placeholder"></span>\n <span class="vjs-control-text"> ' + this.localize('Captions') + '</span>\n '; 17387 } 17388 17389 innerHTML += '</span>'; 17390 17391 var el = _TextTrackMenuItem.prototype.createEl.call(this, type, assign({ 17392 innerHTML: innerHTML 17393 }, props), attrs); 17394 17395 return el; 17396 }; 17397 17398 return SubsCapsMenuItem; 17399}(TextTrackMenuItem); 17400 17401Component.registerComponent('SubsCapsMenuItem', SubsCapsMenuItem); 17402 17403/** 17404 * @file sub-caps-button.js 17405 */ 17406/** 17407 * The button component for toggling and selecting captions and/or subtitles 17408 * 17409 * @extends TextTrackButton 17410 */ 17411 17412var SubsCapsButton = function (_TextTrackButton) { 17413 inherits(SubsCapsButton, _TextTrackButton); 17414 17415 function SubsCapsButton(player) { 17416 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 17417 classCallCheck(this, SubsCapsButton); 17418 17419 // Although North America uses "captions" in most cases for 17420 // "captions and subtitles" other locales use "subtitles" 17421 var _this = possibleConstructorReturn(this, _TextTrackButton.call(this, player, options)); 17422 17423 _this.label_ = 'subtitles'; 17424 if (['en', 'en-us', 'en-ca', 'fr-ca'].indexOf(_this.player_.language_) > -1) { 17425 _this.label_ = 'captions'; 17426 } 17427 _this.menuButton_.controlText(toTitleCase(_this.label_)); 17428 return _this; 17429 } 17430 17431 /** 17432 * Builds the default DOM `className`. 17433 * 17434 * @return {string} 17435 * The DOM `className` for this object. 17436 */ 17437 17438 17439 SubsCapsButton.prototype.buildCSSClass = function buildCSSClass() { 17440 return 'vjs-subs-caps-button ' + _TextTrackButton.prototype.buildCSSClass.call(this); 17441 }; 17442 17443 SubsCapsButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() { 17444 return 'vjs-subs-caps-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this); 17445 }; 17446 17447 /** 17448 * Create caption/subtitles menu items 17449 * 17450 * @return {CaptionSettingsMenuItem[]} 17451 * The array of current menu items. 17452 */ 17453 17454 17455 SubsCapsButton.prototype.createItems = function createItems() { 17456 var items = []; 17457 17458 if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks)) { 17459 items.push(new CaptionSettingsMenuItem(this.player_, { kind: this.label_ })); 17460 17461 this.hideThreshold_ += 1; 17462 } 17463 17464 items = _TextTrackButton.prototype.createItems.call(this, items, SubsCapsMenuItem); 17465 return items; 17466 }; 17467 17468 return SubsCapsButton; 17469}(TextTrackButton); 17470 17471/** 17472 * `kind`s of TextTrack to look for to associate it with this menu. 17473 * 17474 * @type {array} 17475 * @private 17476 */ 17477 17478 17479SubsCapsButton.prototype.kinds_ = ['captions', 'subtitles']; 17480 17481/** 17482 * The text that should display over the `SubsCapsButton`s controls. 17483 * 17484 * 17485 * @type {string} 17486 * @private 17487 */ 17488SubsCapsButton.prototype.controlText_ = 'Subtitles'; 17489 17490Component.registerComponent('SubsCapsButton', SubsCapsButton); 17491 17492/** 17493 * @file audio-track-menu-item.js 17494 */ 17495/** 17496 * An {@link AudioTrack} {@link MenuItem} 17497 * 17498 * @extends MenuItem 17499 */ 17500 17501var AudioTrackMenuItem = function (_MenuItem) { 17502 inherits(AudioTrackMenuItem, _MenuItem); 17503 17504 /** 17505 * Creates an instance of this class. 17506 * 17507 * @param {Player} player 17508 * The `Player` that this class should be attached to. 17509 * 17510 * @param {Object} [options] 17511 * The key/value store of player options. 17512 */ 17513 function AudioTrackMenuItem(player, options) { 17514 classCallCheck(this, AudioTrackMenuItem); 17515 17516 var track = options.track; 17517 var tracks = player.audioTracks(); 17518 17519 // Modify options for parent MenuItem class's init. 17520 options.label = track.label || track.language || 'Unknown'; 17521 options.selected = track.enabled; 17522 17523 var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options)); 17524 17525 _this.track = track; 17526 17527 var changeHandler = function changeHandler() { 17528 for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { 17529 args[_key] = arguments[_key]; 17530 } 17531 17532 _this.handleTracksChange.apply(_this, args); 17533 }; 17534 17535 tracks.addEventListener('change', changeHandler); 17536 _this.on('dispose', function () { 17537 tracks.removeEventListener('change', changeHandler); 17538 }); 17539 return _this; 17540 } 17541 17542 /** 17543 * This gets called when an `AudioTrackMenuItem is "clicked". See {@link ClickableComponent} 17544 * for more detailed information on what a click can be. 17545 * 17546 * @param {EventTarget~Event} [event] 17547 * The `keydown`, `tap`, or `click` event that caused this function to be 17548 * called. 17549 * 17550 * @listens tap 17551 * @listens click 17552 */ 17553 17554 17555 AudioTrackMenuItem.prototype.handleClick = function handleClick(event) { 17556 var tracks = this.player_.audioTracks(); 17557 17558 _MenuItem.prototype.handleClick.call(this, event); 17559 17560 for (var i = 0; i < tracks.length; i++) { 17561 var track = tracks[i]; 17562 17563 track.enabled = track === this.track; 17564 } 17565 }; 17566 17567 /** 17568 * Handle any {@link AudioTrack} change. 17569 * 17570 * @param {EventTarget~Event} [event] 17571 * The {@link AudioTrackList#change} event that caused this to run. 17572 * 17573 * @listens AudioTrackList#change 17574 */ 17575 17576 17577 AudioTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) { 17578 this.selected(this.track.enabled); 17579 }; 17580 17581 return AudioTrackMenuItem; 17582}(MenuItem); 17583 17584Component.registerComponent('AudioTrackMenuItem', AudioTrackMenuItem); 17585 17586/** 17587 * @file audio-track-button.js 17588 */ 17589/** 17590 * The base class for buttons that toggle specific {@link AudioTrack} types. 17591 * 17592 * @extends TrackButton 17593 */ 17594 17595var AudioTrackButton = function (_TrackButton) { 17596 inherits(AudioTrackButton, _TrackButton); 17597 17598 /** 17599 * Creates an instance of this class. 17600 * 17601 * @param {Player} player 17602 * The `Player` that this class should be attached to. 17603 * 17604 * @param {Object} [options={}] 17605 * The key/value store of player options. 17606 */ 17607 function AudioTrackButton(player) { 17608 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 17609 classCallCheck(this, AudioTrackButton); 17610 17611 options.tracks = player.audioTracks(); 17612 17613 return possibleConstructorReturn(this, _TrackButton.call(this, player, options)); 17614 } 17615 17616 /** 17617 * Builds the default DOM `className`. 17618 * 17619 * @return {string} 17620 * The DOM `className` for this object. 17621 */ 17622 17623 17624 AudioTrackButton.prototype.buildCSSClass = function buildCSSClass() { 17625 return 'vjs-audio-button ' + _TrackButton.prototype.buildCSSClass.call(this); 17626 }; 17627 17628 AudioTrackButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() { 17629 return 'vjs-audio-button ' + _TrackButton.prototype.buildWrapperCSSClass.call(this); 17630 }; 17631 17632 /** 17633 * Create a menu item for each audio track 17634 * 17635 * @param {AudioTrackMenuItem[]} [items=[]] 17636 * An array of existing menu items to use. 17637 * 17638 * @return {AudioTrackMenuItem[]} 17639 * An array of menu items 17640 */ 17641 17642 17643 AudioTrackButton.prototype.createItems = function createItems() { 17644 var items = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; 17645 17646 // if there's only one audio track, there no point in showing it 17647 this.hideThreshold_ = 1; 17648 17649 var tracks = this.player_.audioTracks(); 17650 17651 for (var i = 0; i < tracks.length; i++) { 17652 var track = tracks[i]; 17653 17654 items.push(new AudioTrackMenuItem(this.player_, { 17655 track: track, 17656 // MenuItem is selectable 17657 selectable: true 17658 })); 17659 } 17660 17661 return items; 17662 }; 17663 17664 return AudioTrackButton; 17665}(TrackButton); 17666 17667/** 17668 * The text that should display over the `AudioTrackButton`s controls. Added for localization. 17669 * 17670 * @type {string} 17671 * @private 17672 */ 17673 17674 17675AudioTrackButton.prototype.controlText_ = 'Audio Track'; 17676Component.registerComponent('AudioTrackButton', AudioTrackButton); 17677 17678/** 17679 * @file playback-rate-menu-item.js 17680 */ 17681/** 17682 * The specific menu item type for selecting a playback rate. 17683 * 17684 * @extends MenuItem 17685 */ 17686 17687var PlaybackRateMenuItem = function (_MenuItem) { 17688 inherits(PlaybackRateMenuItem, _MenuItem); 17689 17690 /** 17691 * Creates an instance of this class. 17692 * 17693 * @param {Player} player 17694 * The `Player` that this class should be attached to. 17695 * 17696 * @param {Object} [options] 17697 * The key/value store of player options. 17698 */ 17699 function PlaybackRateMenuItem(player, options) { 17700 classCallCheck(this, PlaybackRateMenuItem); 17701 17702 var label = options.rate; 17703 var rate = parseFloat(label, 10); 17704 17705 // Modify options for parent MenuItem class's init. 17706 options.label = label; 17707 options.selected = rate === 1; 17708 options.selectable = true; 17709 17710 var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options)); 17711 17712 _this.label = label; 17713 _this.rate = rate; 17714 17715 _this.on(player, 'ratechange', _this.update); 17716 return _this; 17717 } 17718 17719 /** 17720 * This gets called when an `PlaybackRateMenuItem` is "clicked". See 17721 * {@link ClickableComponent} for more detailed information on what a click can be. 17722 * 17723 * @param {EventTarget~Event} [event] 17724 * The `keydown`, `tap`, or `click` event that caused this function to be 17725 * called. 17726 * 17727 * @listens tap 17728 * @listens click 17729 */ 17730 17731 17732 PlaybackRateMenuItem.prototype.handleClick = function handleClick(event) { 17733 _MenuItem.prototype.handleClick.call(this); 17734 this.player().playbackRate(this.rate); 17735 }; 17736 17737 /** 17738 * Update the PlaybackRateMenuItem when the playbackrate changes. 17739 * 17740 * @param {EventTarget~Event} [event] 17741 * The `ratechange` event that caused this function to run. 17742 * 17743 * @listens Player#ratechange 17744 */ 17745 17746 17747 PlaybackRateMenuItem.prototype.update = function update(event) { 17748 this.selected(this.player().playbackRate() === this.rate); 17749 }; 17750 17751 return PlaybackRateMenuItem; 17752}(MenuItem); 17753 17754/** 17755 * The text that should display over the `PlaybackRateMenuItem`s controls. Added for localization. 17756 * 17757 * @type {string} 17758 * @private 17759 */ 17760 17761 17762PlaybackRateMenuItem.prototype.contentElType = 'button'; 17763 17764Component.registerComponent('PlaybackRateMenuItem', PlaybackRateMenuItem); 17765 17766/** 17767 * @file playback-rate-menu-button.js 17768 */ 17769/** 17770 * The component for controlling the playback rate. 17771 * 17772 * @extends MenuButton 17773 */ 17774 17775var PlaybackRateMenuButton = function (_MenuButton) { 17776 inherits(PlaybackRateMenuButton, _MenuButton); 17777 17778 /** 17779 * Creates an instance of this class. 17780 * 17781 * @param {Player} player 17782 * The `Player` that this class should be attached to. 17783 * 17784 * @param {Object} [options] 17785 * The key/value store of player options. 17786 */ 17787 function PlaybackRateMenuButton(player, options) { 17788 classCallCheck(this, PlaybackRateMenuButton); 17789 17790 var _this = possibleConstructorReturn(this, _MenuButton.call(this, player, options)); 17791 17792 _this.updateVisibility(); 17793 _this.updateLabel(); 17794 17795 _this.on(player, 'loadstart', _this.updateVisibility); 17796 _this.on(player, 'ratechange', _this.updateLabel); 17797 return _this; 17798 } 17799 17800 /** 17801 * Create the `Component`'s DOM element 17802 * 17803 * @return {Element} 17804 * The element that was created. 17805 */ 17806 17807 17808 PlaybackRateMenuButton.prototype.createEl = function createEl$$1() { 17809 var el = _MenuButton.prototype.createEl.call(this); 17810 17811 this.labelEl_ = createEl('div', { 17812 className: 'vjs-playback-rate-value', 17813 innerHTML: '1x' 17814 }); 17815 17816 el.appendChild(this.labelEl_); 17817 17818 return el; 17819 }; 17820 17821 PlaybackRateMenuButton.prototype.dispose = function dispose() { 17822 this.labelEl_ = null; 17823 17824 _MenuButton.prototype.dispose.call(this); 17825 }; 17826 17827 /** 17828 * Builds the default DOM `className`. 17829 * 17830 * @return {string} 17831 * The DOM `className` for this object. 17832 */ 17833 17834 17835 PlaybackRateMenuButton.prototype.buildCSSClass = function buildCSSClass() { 17836 return 'vjs-playback-rate ' + _MenuButton.prototype.buildCSSClass.call(this); 17837 }; 17838 17839 PlaybackRateMenuButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() { 17840 return 'vjs-playback-rate ' + _MenuButton.prototype.buildWrapperCSSClass.call(this); 17841 }; 17842 17843 /** 17844 * Create the playback rate menu 17845 * 17846 * @return {Menu} 17847 * Menu object populated with {@link PlaybackRateMenuItem}s 17848 */ 17849 17850 17851 PlaybackRateMenuButton.prototype.createMenu = function createMenu() { 17852 var menu = new Menu(this.player()); 17853 var rates = this.playbackRates(); 17854 17855 if (rates) { 17856 for (var i = rates.length - 1; i >= 0; i--) { 17857 menu.addChild(new PlaybackRateMenuItem(this.player(), { rate: rates[i] + 'x' })); 17858 } 17859 } 17860 17861 return menu; 17862 }; 17863 17864 /** 17865 * Updates ARIA accessibility attributes 17866 */ 17867 17868 17869 PlaybackRateMenuButton.prototype.updateARIAAttributes = function updateARIAAttributes() { 17870 // Current playback rate 17871 this.el().setAttribute('aria-valuenow', this.player().playbackRate()); 17872 }; 17873 17874 /** 17875 * This gets called when an `PlaybackRateMenuButton` is "clicked". See 17876 * {@link ClickableComponent} for more detailed information on what a click can be. 17877 * 17878 * @param {EventTarget~Event} [event] 17879 * The `keydown`, `tap`, or `click` event that caused this function to be 17880 * called. 17881 * 17882 * @listens tap 17883 * @listens click 17884 */ 17885 17886 17887 PlaybackRateMenuButton.prototype.handleClick = function handleClick(event) { 17888 // select next rate option 17889 var currentRate = this.player().playbackRate(); 17890 var rates = this.playbackRates(); 17891 17892 // this will select first one if the last one currently selected 17893 var newRate = rates[0]; 17894 17895 for (var i = 0; i < rates.length; i++) { 17896 if (rates[i] > currentRate) { 17897 newRate = rates[i]; 17898 break; 17899 } 17900 } 17901 this.player().playbackRate(newRate); 17902 }; 17903 17904 /** 17905 * Get possible playback rates 17906 * 17907 * @return {Array} 17908 * All possible playback rates 17909 */ 17910 17911 17912 PlaybackRateMenuButton.prototype.playbackRates = function playbackRates() { 17913 return this.options_.playbackRates || this.options_.playerOptions && this.options_.playerOptions.playbackRates; 17914 }; 17915 17916 /** 17917 * Get whether playback rates is supported by the tech 17918 * and an array of playback rates exists 17919 * 17920 * @return {boolean} 17921 * Whether changing playback rate is supported 17922 */ 17923 17924 17925 PlaybackRateMenuButton.prototype.playbackRateSupported = function playbackRateSupported() { 17926 return this.player().tech_ && this.player().tech_.featuresPlaybackRate && this.playbackRates() && this.playbackRates().length > 0; 17927 }; 17928 17929 /** 17930 * Hide playback rate controls when they're no playback rate options to select 17931 * 17932 * @param {EventTarget~Event} [event] 17933 * The event that caused this function to run. 17934 * 17935 * @listens Player#loadstart 17936 */ 17937 17938 17939 PlaybackRateMenuButton.prototype.updateVisibility = function updateVisibility(event) { 17940 if (this.playbackRateSupported()) { 17941 this.removeClass('vjs-hidden'); 17942 } else { 17943 this.addClass('vjs-hidden'); 17944 } 17945 }; 17946 17947 /** 17948 * Update button label when rate changed 17949 * 17950 * @param {EventTarget~Event} [event] 17951 * The event that caused this function to run. 17952 * 17953 * @listens Player#ratechange 17954 */ 17955 17956 17957 PlaybackRateMenuButton.prototype.updateLabel = function updateLabel(event) { 17958 if (this.playbackRateSupported()) { 17959 this.labelEl_.innerHTML = this.player().playbackRate() + 'x'; 17960 } 17961 }; 17962 17963 return PlaybackRateMenuButton; 17964}(MenuButton); 17965 17966/** 17967 * The text that should display over the `FullscreenToggle`s controls. Added for localization. 17968 * 17969 * @type {string} 17970 * @private 17971 */ 17972 17973 17974PlaybackRateMenuButton.prototype.controlText_ = 'Playback Rate'; 17975 17976Component.registerComponent('PlaybackRateMenuButton', PlaybackRateMenuButton); 17977 17978/** 17979 * @file spacer.js 17980 */ 17981/** 17982 * Just an empty spacer element that can be used as an append point for plugins, etc. 17983 * Also can be used to create space between elements when necessary. 17984 * 17985 * @extends Component 17986 */ 17987 17988var Spacer = function (_Component) { 17989 inherits(Spacer, _Component); 17990 17991 function Spacer() { 17992 classCallCheck(this, Spacer); 17993 return possibleConstructorReturn(this, _Component.apply(this, arguments)); 17994 } 17995 17996 /** 17997 * Builds the default DOM `className`. 17998 * 17999 * @return {string} 18000 * The DOM `className` for this object. 18001 */ 18002 Spacer.prototype.buildCSSClass = function buildCSSClass() { 18003 return 'vjs-spacer ' + _Component.prototype.buildCSSClass.call(this); 18004 }; 18005 18006 /** 18007 * Create the `Component`'s DOM element 18008 * 18009 * @return {Element} 18010 * The element that was created. 18011 */ 18012 18013 18014 Spacer.prototype.createEl = function createEl() { 18015 return _Component.prototype.createEl.call(this, 'div', { 18016 className: this.buildCSSClass() 18017 }); 18018 }; 18019 18020 return Spacer; 18021}(Component); 18022 18023Component.registerComponent('Spacer', Spacer); 18024 18025/** 18026 * @file custom-control-spacer.js 18027 */ 18028/** 18029 * Spacer specifically meant to be used as an insertion point for new plugins, etc. 18030 * 18031 * @extends Spacer 18032 */ 18033 18034var CustomControlSpacer = function (_Spacer) { 18035 inherits(CustomControlSpacer, _Spacer); 18036 18037 function CustomControlSpacer() { 18038 classCallCheck(this, CustomControlSpacer); 18039 return possibleConstructorReturn(this, _Spacer.apply(this, arguments)); 18040 } 18041 18042 /** 18043 * Builds the default DOM `className`. 18044 * 18045 * @return {string} 18046 * The DOM `className` for this object. 18047 */ 18048 CustomControlSpacer.prototype.buildCSSClass = function buildCSSClass() { 18049 return 'vjs-custom-control-spacer ' + _Spacer.prototype.buildCSSClass.call(this); 18050 }; 18051 18052 /** 18053 * Create the `Component`'s DOM element 18054 * 18055 * @return {Element} 18056 * The element that was created. 18057 */ 18058 18059 18060 CustomControlSpacer.prototype.createEl = function createEl() { 18061 var el = _Spacer.prototype.createEl.call(this, { 18062 className: this.buildCSSClass() 18063 }); 18064 18065 // No-flex/table-cell mode requires there be some content 18066 // in the cell to fill the remaining space of the table. 18067 el.innerHTML = '\xA0'; 18068 return el; 18069 }; 18070 18071 return CustomControlSpacer; 18072}(Spacer); 18073 18074Component.registerComponent('CustomControlSpacer', CustomControlSpacer); 18075 18076/** 18077 * @file control-bar.js 18078 */ 18079// Required children 18080/** 18081 * Container of main controls. 18082 * 18083 * @extends Component 18084 */ 18085 18086var ControlBar = function (_Component) { 18087 inherits(ControlBar, _Component); 18088 18089 function ControlBar() { 18090 classCallCheck(this, ControlBar); 18091 return possibleConstructorReturn(this, _Component.apply(this, arguments)); 18092 } 18093 18094 /** 18095 * Create the `Component`'s DOM element 18096 * 18097 * @return {Element} 18098 * The element that was created. 18099 */ 18100 ControlBar.prototype.createEl = function createEl() { 18101 return _Component.prototype.createEl.call(this, 'div', { 18102 className: 'vjs-control-bar', 18103 dir: 'ltr' 18104 }, { 18105 // The control bar is a group, but we don't aria-label it to avoid 18106 // over-announcing by JAWS 18107 role: 'group' 18108 }); 18109 }; 18110 18111 return ControlBar; 18112}(Component); 18113 18114/** 18115 * Default options for `ControlBar` 18116 * 18117 * @type {Object} 18118 * @private 18119 */ 18120 18121 18122ControlBar.prototype.options_ = { 18123 children: ['playToggle', 'volumePanel', 'currentTimeDisplay', 'timeDivider', 'durationDisplay', 'progressControl', 'liveDisplay', 'remainingTimeDisplay', 'customControlSpacer', 'playbackRateMenuButton', 'chaptersButton', 'descriptionsButton', 'subsCapsButton', 'audioTrackButton', 'fullscreenToggle'] 18124}; 18125 18126Component.registerComponent('ControlBar', ControlBar); 18127 18128/** 18129 * @file error-display.js 18130 */ 18131/** 18132 * A display that indicates an error has occurred. This means that the video 18133 * is unplayable. 18134 * 18135 * @extends ModalDialog 18136 */ 18137 18138var ErrorDisplay = function (_ModalDialog) { 18139 inherits(ErrorDisplay, _ModalDialog); 18140 18141 /** 18142 * Creates an instance of this class. 18143 * 18144 * @param {Player} player 18145 * The `Player` that this class should be attached to. 18146 * 18147 * @param {Object} [options] 18148 * The key/value store of player options. 18149 */ 18150 function ErrorDisplay(player, options) { 18151 classCallCheck(this, ErrorDisplay); 18152 18153 var _this = possibleConstructorReturn(this, _ModalDialog.call(this, player, options)); 18154 18155 _this.on(player, 'error', _this.open); 18156 return _this; 18157 } 18158 18159 /** 18160 * Builds the default DOM `className`. 18161 * 18162 * @return {string} 18163 * The DOM `className` for this object. 18164 * 18165 * @deprecated Since version 5. 18166 */ 18167 18168 18169 ErrorDisplay.prototype.buildCSSClass = function buildCSSClass() { 18170 return 'vjs-error-display ' + _ModalDialog.prototype.buildCSSClass.call(this); 18171 }; 18172 18173 /** 18174 * Gets the localized error message based on the `Player`s error. 18175 * 18176 * @return {string} 18177 * The `Player`s error message localized or an empty string. 18178 */ 18179 18180 18181 ErrorDisplay.prototype.content = function content() { 18182 var error = this.player().error(); 18183 18184 return error ? this.localize(error.message) : ''; 18185 }; 18186 18187 return ErrorDisplay; 18188}(ModalDialog); 18189 18190/** 18191 * The default options for an `ErrorDisplay`. 18192 * 18193 * @private 18194 */ 18195 18196 18197ErrorDisplay.prototype.options_ = mergeOptions(ModalDialog.prototype.options_, { 18198 pauseOnOpen: false, 18199 fillAlways: true, 18200 temporary: false, 18201 uncloseable: true 18202}); 18203 18204Component.registerComponent('ErrorDisplay', ErrorDisplay); 18205 18206/** 18207 * @file text-track-settings.js 18208 */ 18209var LOCAL_STORAGE_KEY = 'vjs-text-track-settings'; 18210 18211var COLOR_BLACK = ['#000', 'Black']; 18212var COLOR_BLUE = ['#00F', 'Blue']; 18213var COLOR_CYAN = ['#0FF', 'Cyan']; 18214var COLOR_GREEN = ['#0F0', 'Green']; 18215var COLOR_MAGENTA = ['#F0F', 'Magenta']; 18216var COLOR_RED = ['#F00', 'Red']; 18217var COLOR_WHITE = ['#FFF', 'White']; 18218var COLOR_YELLOW = ['#FF0', 'Yellow']; 18219 18220var OPACITY_OPAQUE = ['1', 'Opaque']; 18221var OPACITY_SEMI = ['0.5', 'Semi-Transparent']; 18222var OPACITY_TRANS = ['0', 'Transparent']; 18223 18224// Configuration for the various <select> elements in the DOM of this component. 18225// 18226// Possible keys include: 18227// 18228// `default`: 18229// The default option index. Only needs to be provided if not zero. 18230// `parser`: 18231// A function which is used to parse the value from the selected option in 18232// a customized way. 18233// `selector`: 18234// The selector used to find the associated <select> element. 18235var selectConfigs = { 18236 backgroundColor: { 18237 selector: '.vjs-bg-color > select', 18238 id: 'captions-background-color-%s', 18239 label: 'Color', 18240 options: [COLOR_BLACK, COLOR_WHITE, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_MAGENTA, COLOR_CYAN] 18241 }, 18242 18243 backgroundOpacity: { 18244 selector: '.vjs-bg-opacity > select', 18245 id: 'captions-background-opacity-%s', 18246 label: 'Transparency', 18247 options: [OPACITY_OPAQUE, OPACITY_SEMI, OPACITY_TRANS] 18248 }, 18249 18250 color: { 18251 selector: '.vjs-fg-color > select', 18252 id: 'captions-foreground-color-%s', 18253 label: 'Color', 18254 options: [COLOR_WHITE, COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_MAGENTA, COLOR_CYAN] 18255 }, 18256 18257 edgeStyle: { 18258 selector: '.vjs-edge-style > select', 18259 id: '%s', 18260 label: 'Text Edge Style', 18261 options: [['none', 'None'], ['raised', 'Raised'], ['depressed', 'Depressed'], ['uniform', 'Uniform'], ['dropshadow', 'Dropshadow']] 18262 }, 18263 18264 fontFamily: { 18265 selector: '.vjs-font-family > select', 18266 id: 'captions-font-family-%s', 18267 label: 'Font Family', 18268 options: [['proportionalSansSerif', 'Proportional Sans-Serif'], ['monospaceSansSerif', 'Monospace Sans-Serif'], ['proportionalSerif', 'Proportional Serif'], ['monospaceSerif', 'Monospace Serif'], ['casual', 'Casual'], ['script', 'Script'], ['small-caps', 'Small Caps']] 18269 }, 18270 18271 fontPercent: { 18272 selector: '.vjs-font-percent > select', 18273 id: 'captions-font-size-%s', 18274 label: 'Font Size', 18275 options: [['0.50', '50%'], ['0.75', '75%'], ['1.00', '100%'], ['1.25', '125%'], ['1.50', '150%'], ['1.75', '175%'], ['2.00', '200%'], ['3.00', '300%'], ['4.00', '400%']], 18276 'default': 2, 18277 parser: function parser(v) { 18278 return v === '1.00' ? null : Number(v); 18279 } 18280 }, 18281 18282 textOpacity: { 18283 selector: '.vjs-text-opacity > select', 18284 id: 'captions-foreground-opacity-%s', 18285 label: 'Transparency', 18286 options: [OPACITY_OPAQUE, OPACITY_SEMI] 18287 }, 18288 18289 // Options for this object are defined below. 18290 windowColor: { 18291 selector: '.vjs-window-color > select', 18292 id: 'captions-window-color-%s', 18293 label: 'Color' 18294 }, 18295 18296 // Options for this object are defined below. 18297 windowOpacity: { 18298 selector: '.vjs-window-opacity > select', 18299 id: 'captions-window-opacity-%s', 18300 label: 'Transparency', 18301 options: [OPACITY_TRANS, OPACITY_SEMI, OPACITY_OPAQUE] 18302 } 18303}; 18304 18305selectConfigs.windowColor.options = selectConfigs.backgroundColor.options; 18306 18307/** 18308 * Get the actual value of an option. 18309 * 18310 * @param {string} value 18311 * The value to get 18312 * 18313 * @param {Function} [parser] 18314 * Optional function to adjust the value. 18315 * 18316 * @return {Mixed} 18317 * - Will be `undefined` if no value exists 18318 * - Will be `undefined` if the given value is "none". 18319 * - Will be the actual value otherwise. 18320 * 18321 * @private 18322 */ 18323function parseOptionValue(value, parser) { 18324 if (parser) { 18325 value = parser(value); 18326 } 18327 18328 if (value && value !== 'none') { 18329 return value; 18330 } 18331} 18332 18333/** 18334 * Gets the value of the selected <option> element within a <select> element. 18335 * 18336 * @param {Element} el 18337 * the element to look in 18338 * 18339 * @param {Function} [parser] 18340 * Optional function to adjust the value. 18341 * 18342 * @return {Mixed} 18343 * - Will be `undefined` if no value exists 18344 * - Will be `undefined` if the given value is "none". 18345 * - Will be the actual value otherwise. 18346 * 18347 * @private 18348 */ 18349function getSelectedOptionValue(el, parser) { 18350 var value = el.options[el.options.selectedIndex].value; 18351 18352 return parseOptionValue(value, parser); 18353} 18354 18355/** 18356 * Sets the selected <option> element within a <select> element based on a 18357 * given value. 18358 * 18359 * @param {Element} el 18360 * The element to look in. 18361 * 18362 * @param {string} value 18363 * the property to look on. 18364 * 18365 * @param {Function} [parser] 18366 * Optional function to adjust the value before comparing. 18367 * 18368 * @private 18369 */ 18370function setSelectedOption(el, value, parser) { 18371 if (!value) { 18372 return; 18373 } 18374 18375 for (var i = 0; i < el.options.length; i++) { 18376 if (parseOptionValue(el.options[i].value, parser) === value) { 18377 el.selectedIndex = i; 18378 break; 18379 } 18380 } 18381} 18382 18383/** 18384 * Manipulate Text Tracks settings. 18385 * 18386 * @extends ModalDialog 18387 */ 18388 18389var TextTrackSettings = function (_ModalDialog) { 18390 inherits(TextTrackSettings, _ModalDialog); 18391 18392 /** 18393 * Creates an instance of this class. 18394 * 18395 * @param {Player} player 18396 * The `Player` that this class should be attached to. 18397 * 18398 * @param {Object} [options] 18399 * The key/value store of player options. 18400 */ 18401 function TextTrackSettings(player, options) { 18402 classCallCheck(this, TextTrackSettings); 18403 18404 options.temporary = false; 18405 18406 var _this = possibleConstructorReturn(this, _ModalDialog.call(this, player, options)); 18407 18408 _this.updateDisplay = bind(_this, _this.updateDisplay); 18409 18410 // fill the modal and pretend we have opened it 18411 _this.fill(); 18412 _this.hasBeenOpened_ = _this.hasBeenFilled_ = true; 18413 18414 _this.endDialog = createEl('p', { 18415 className: 'vjs-control-text', 18416 textContent: _this.localize('End of dialog window.') 18417 }); 18418 _this.el().appendChild(_this.endDialog); 18419 18420 _this.setDefaults(); 18421 18422 // Grab `persistTextTrackSettings` from the player options if not passed in child options 18423 if (options.persistTextTrackSettings === undefined) { 18424 _this.options_.persistTextTrackSettings = _this.options_.playerOptions.persistTextTrackSettings; 18425 } 18426 18427 _this.on(_this.$('.vjs-done-button'), 'click', function () { 18428 _this.saveSettings(); 18429 _this.close(); 18430 }); 18431 18432 _this.on(_this.$('.vjs-default-button'), 'click', function () { 18433 _this.setDefaults(); 18434 _this.updateDisplay(); 18435 }); 18436 18437 each(selectConfigs, function (config) { 18438 _this.on(_this.$(config.selector), 'change', _this.updateDisplay); 18439 }); 18440 18441 if (_this.options_.persistTextTrackSettings) { 18442 _this.restoreSettings(); 18443 } 18444 return _this; 18445 } 18446 18447 TextTrackSettings.prototype.dispose = function dispose() { 18448 this.endDialog = null; 18449 18450 _ModalDialog.prototype.dispose.call(this); 18451 }; 18452 18453 /** 18454 * Create a <select> element with configured options. 18455 * 18456 * @param {string} key 18457 * Configuration key to use during creation. 18458 * 18459 * @return {string} 18460 * An HTML string. 18461 * 18462 * @private 18463 */ 18464 18465 18466 TextTrackSettings.prototype.createElSelect_ = function createElSelect_(key) { 18467 var _this2 = this; 18468 18469 var legendId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; 18470 var type = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'label'; 18471 18472 var config = selectConfigs[key]; 18473 var id = config.id.replace('%s', this.id_); 18474 18475 return ['<' + type + ' id="' + id + '" class="' + (type === 'label' ? 'vjs-label' : '') + '">', this.localize(config.label), '</' + type + '>', '<select aria-labelledby="' + (legendId !== '' ? legendId + ' ' : '') + id + '">'].concat(config.options.map(function (o) { 18476 var optionId = id + '-' + o[1]; 18477 18478 return ['<option id="' + optionId + '" value="' + o[0] + '" ', 'aria-labelledby="' + (legendId !== '' ? legendId + ' ' : '') + id + ' ' + optionId + '">', _this2.localize(o[1]), '</option>'].join(''); 18479 })).concat('</select>').join(''); 18480 }; 18481 18482 /** 18483 * Create foreground color element for the component 18484 * 18485 * @return {string} 18486 * An HTML string. 18487 * 18488 * @private 18489 */ 18490 18491 18492 TextTrackSettings.prototype.createElFgColor_ = function createElFgColor_() { 18493 var legendId = 'captions-text-legend-' + this.id_; 18494 18495 return ['<fieldset class="vjs-fg-color vjs-track-setting">', '<legend id="' + legendId + '">', this.localize('Text'), '</legend>', this.createElSelect_('color', legendId), '<span class="vjs-text-opacity vjs-opacity">', this.createElSelect_('textOpacity', legendId), '</span>', '</fieldset>'].join(''); 18496 }; 18497 18498 /** 18499 * Create background color element for the component 18500 * 18501 * @return {string} 18502 * An HTML string. 18503 * 18504 * @private 18505 */ 18506 18507 18508 TextTrackSettings.prototype.createElBgColor_ = function createElBgColor_() { 18509 var legendId = 'captions-background-' + this.id_; 18510 18511 return ['<fieldset class="vjs-bg-color vjs-track-setting">', '<legend id="' + legendId + '">', this.localize('Background'), '</legend>', this.createElSelect_('backgroundColor', legendId), '<span class="vjs-bg-opacity vjs-opacity">', this.createElSelect_('backgroundOpacity', legendId), '</span>', '</fieldset>'].join(''); 18512 }; 18513 18514 /** 18515 * Create window color element for the component 18516 * 18517 * @return {string} 18518 * An HTML string. 18519 * 18520 * @private 18521 */ 18522 18523 18524 TextTrackSettings.prototype.createElWinColor_ = function createElWinColor_() { 18525 var legendId = 'captions-window-' + this.id_; 18526 18527 return ['<fieldset class="vjs-window-color vjs-track-setting">', '<legend id="' + legendId + '">', this.localize('Window'), '</legend>', this.createElSelect_('windowColor', legendId), '<span class="vjs-window-opacity vjs-opacity">', this.createElSelect_('windowOpacity', legendId), '</span>', '</fieldset>'].join(''); 18528 }; 18529 18530 /** 18531 * Create color elements for the component 18532 * 18533 * @return {Element} 18534 * The element that was created 18535 * 18536 * @private 18537 */ 18538 18539 18540 TextTrackSettings.prototype.createElColors_ = function createElColors_() { 18541 return createEl('div', { 18542 className: 'vjs-track-settings-colors', 18543 innerHTML: [this.createElFgColor_(), this.createElBgColor_(), this.createElWinColor_()].join('') 18544 }); 18545 }; 18546 18547 /** 18548 * Create font elements for the component 18549 * 18550 * @return {Element} 18551 * The element that was created. 18552 * 18553 * @private 18554 */ 18555 18556 18557 TextTrackSettings.prototype.createElFont_ = function createElFont_() { 18558 return createEl('div', { 18559 className: 'vjs-track-settings-font">', 18560 innerHTML: ['<fieldset class="vjs-font-percent vjs-track-setting">', this.createElSelect_('fontPercent', '', 'legend'), '</fieldset>', '<fieldset class="vjs-edge-style vjs-track-setting">', this.createElSelect_('edgeStyle', '', 'legend'), '</fieldset>', '<fieldset class="vjs-font-family vjs-track-setting">', this.createElSelect_('fontFamily', '', 'legend'), '</fieldset>'].join('') 18561 }); 18562 }; 18563 18564 /** 18565 * Create controls for the component 18566 * 18567 * @return {Element} 18568 * The element that was created. 18569 * 18570 * @private 18571 */ 18572 18573 18574 TextTrackSettings.prototype.createElControls_ = function createElControls_() { 18575 var defaultsDescription = this.localize('restore all settings to the default values'); 18576 18577 return createEl('div', { 18578 className: 'vjs-track-settings-controls', 18579 innerHTML: ['<button class="vjs-default-button" title="' + defaultsDescription + '">', this.localize('Reset'), '<span class="vjs-control-text"> ' + defaultsDescription + '</span>', '</button>', '<button class="vjs-done-button">' + this.localize('Done') + '</button>'].join('') 18580 }); 18581 }; 18582 18583 TextTrackSettings.prototype.content = function content() { 18584 return [this.createElColors_(), this.createElFont_(), this.createElControls_()]; 18585 }; 18586 18587 TextTrackSettings.prototype.label = function label() { 18588 return this.localize('Caption Settings Dialog'); 18589 }; 18590 18591 TextTrackSettings.prototype.description = function description() { 18592 return this.localize('Beginning of dialog window. Escape will cancel and close the window.'); 18593 }; 18594 18595 TextTrackSettings.prototype.buildCSSClass = function buildCSSClass() { 18596 return _ModalDialog.prototype.buildCSSClass.call(this) + ' vjs-text-track-settings'; 18597 }; 18598 18599 /** 18600 * Gets an object of text track settings (or null). 18601 * 18602 * @return {Object} 18603 * An object with config values parsed from the DOM or localStorage. 18604 */ 18605 18606 18607 TextTrackSettings.prototype.getValues = function getValues() { 18608 var _this3 = this; 18609 18610 return reduce(selectConfigs, function (accum, config, key) { 18611 var value = getSelectedOptionValue(_this3.$(config.selector), config.parser); 18612 18613 if (value !== undefined) { 18614 accum[key] = value; 18615 } 18616 18617 return accum; 18618 }, {}); 18619 }; 18620 18621 /** 18622 * Sets text track settings from an object of values. 18623 * 18624 * @param {Object} values 18625 * An object with config values parsed from the DOM or localStorage. 18626 */ 18627 18628 18629 TextTrackSettings.prototype.setValues = function setValues(values) { 18630 var _this4 = this; 18631 18632 each(selectConfigs, function (config, key) { 18633 setSelectedOption(_this4.$(config.selector), values[key], config.parser); 18634 }); 18635 }; 18636 18637 /** 18638 * Sets all `<select>` elements to their default values. 18639 */ 18640 18641 18642 TextTrackSettings.prototype.setDefaults = function setDefaults() { 18643 var _this5 = this; 18644 18645 each(selectConfigs, function (config) { 18646 var index = config.hasOwnProperty('default') ? config['default'] : 0; 18647 18648 _this5.$(config.selector).selectedIndex = index; 18649 }); 18650 }; 18651 18652 /** 18653 * Restore texttrack settings from localStorage 18654 */ 18655 18656 18657 TextTrackSettings.prototype.restoreSettings = function restoreSettings() { 18658 var values = void 0; 18659 18660 try { 18661 values = JSON.parse(window_1.localStorage.getItem(LOCAL_STORAGE_KEY)); 18662 } catch (err) { 18663 log$1.warn(err); 18664 } 18665 18666 if (values) { 18667 this.setValues(values); 18668 } 18669 }; 18670 18671 /** 18672 * Save text track settings to localStorage 18673 */ 18674 18675 18676 TextTrackSettings.prototype.saveSettings = function saveSettings() { 18677 if (!this.options_.persistTextTrackSettings) { 18678 return; 18679 } 18680 18681 var values = this.getValues(); 18682 18683 try { 18684 if (Object.keys(values).length) { 18685 window_1.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(values)); 18686 } else { 18687 window_1.localStorage.removeItem(LOCAL_STORAGE_KEY); 18688 } 18689 } catch (err) { 18690 log$1.warn(err); 18691 } 18692 }; 18693 18694 /** 18695 * Update display of text track settings 18696 */ 18697 18698 18699 TextTrackSettings.prototype.updateDisplay = function updateDisplay() { 18700 var ttDisplay = this.player_.getChild('textTrackDisplay'); 18701 18702 if (ttDisplay) { 18703 ttDisplay.updateDisplay(); 18704 } 18705 }; 18706 18707 /** 18708 * conditionally blur the element and refocus the captions button 18709 * 18710 * @private 18711 */ 18712 18713 18714 TextTrackSettings.prototype.conditionalBlur_ = function conditionalBlur_() { 18715 this.previouslyActiveEl_ = null; 18716 this.off(document_1, 'keydown', this.handleKeyDown); 18717 18718 var cb = this.player_.controlBar; 18719 var subsCapsBtn = cb && cb.subsCapsButton; 18720 var ccBtn = cb && cb.captionsButton; 18721 18722 if (subsCapsBtn) { 18723 subsCapsBtn.focus(); 18724 } else if (ccBtn) { 18725 ccBtn.focus(); 18726 } 18727 }; 18728 18729 return TextTrackSettings; 18730}(ModalDialog); 18731 18732Component.registerComponent('TextTrackSettings', TextTrackSettings); 18733 18734var _templateObject$2 = taggedTemplateLiteralLoose(['Text Tracks are being loaded from another origin but the crossorigin attribute isn\'t used.\n This may prevent text tracks from loading.'], ['Text Tracks are being loaded from another origin but the crossorigin attribute isn\'t used.\n This may prevent text tracks from loading.']); 18735 18736/** 18737 * @file html5.js 18738 */ 18739/** 18740 * HTML5 Media Controller - Wrapper for HTML5 Media API 18741 * 18742 * @mixes Tech~SouceHandlerAdditions 18743 * @extends Tech 18744 */ 18745 18746var Html5 = function (_Tech) { 18747 inherits(Html5, _Tech); 18748 18749 /** 18750 * Create an instance of this Tech. 18751 * 18752 * @param {Object} [options] 18753 * The key/value store of player options. 18754 * 18755 * @param {Component~ReadyCallback} ready 18756 * Callback function to call when the `HTML5` Tech is ready. 18757 */ 18758 function Html5(options, ready) { 18759 classCallCheck(this, Html5); 18760 18761 var _this = possibleConstructorReturn(this, _Tech.call(this, options, ready)); 18762 18763 var source = options.source; 18764 var crossoriginTracks = false; 18765 18766 // Set the source if one is provided 18767 // 1) Check if the source is new (if not, we want to keep the original so playback isn't interrupted) 18768 // 2) Check to see if the network state of the tag was failed at init, and if so, reset the source 18769 // anyway so the error gets fired. 18770 if (source && (_this.el_.currentSrc !== source.src || options.tag && options.tag.initNetworkState_ === 3)) { 18771 _this.setSource(source); 18772 } else { 18773 _this.handleLateInit_(_this.el_); 18774 } 18775 18776 if (_this.el_.hasChildNodes()) { 18777 18778 var nodes = _this.el_.childNodes; 18779 var nodesLength = nodes.length; 18780 var removeNodes = []; 18781 18782 while (nodesLength--) { 18783 var node = nodes[nodesLength]; 18784 var nodeName = node.nodeName.toLowerCase(); 18785 18786 if (nodeName === 'track') { 18787 if (!_this.featuresNativeTextTracks) { 18788 // Empty video tag tracks so the built-in player doesn't use them also. 18789 // This may not be fast enough to stop HTML5 browsers from reading the tags 18790 // so we'll need to turn off any default tracks if we're manually doing 18791 // captions and subtitles. videoElement.textTracks 18792 removeNodes.push(node); 18793 } else { 18794 // store HTMLTrackElement and TextTrack to remote list 18795 _this.remoteTextTrackEls().addTrackElement_(node); 18796 _this.remoteTextTracks().addTrack(node.track); 18797 _this.textTracks().addTrack(node.track); 18798 if (!crossoriginTracks && !_this.el_.hasAttribute('crossorigin') && isCrossOrigin(node.src)) { 18799 crossoriginTracks = true; 18800 } 18801 } 18802 } 18803 } 18804 18805 for (var i = 0; i < removeNodes.length; i++) { 18806 _this.el_.removeChild(removeNodes[i]); 18807 } 18808 } 18809 18810 _this.proxyNativeTracks_(); 18811 if (_this.featuresNativeTextTracks && crossoriginTracks) { 18812 log$1.warn(tsml(_templateObject$2)); 18813 } 18814 18815 // prevent iOS Safari from disabling metadata text tracks during native playback 18816 _this.restoreMetadataTracksInIOSNativePlayer_(); 18817 18818 // Determine if native controls should be used 18819 // Our goal should be to get the custom controls on mobile solid everywhere 18820 // so we can remove this all together. Right now this will block custom 18821 // controls on touch enabled laptops like the Chrome Pixel 18822 if ((TOUCH_ENABLED || IS_IPHONE || IS_NATIVE_ANDROID) && options.nativeControlsForTouch === true) { 18823 _this.setControls(true); 18824 } 18825 18826 // on iOS, we want to proxy `webkitbeginfullscreen` and `webkitendfullscreen` 18827 // into a `fullscreenchange` event 18828 _this.proxyWebkitFullscreen_(); 18829 18830 _this.triggerReady(); 18831 return _this; 18832 } 18833 18834 /** 18835 * Dispose of `HTML5` media element and remove all tracks. 18836 */ 18837 18838 18839 Html5.prototype.dispose = function dispose() { 18840 Html5.disposeMediaElement(this.el_); 18841 this.options_ = null; 18842 18843 // tech will handle clearing of the emulated track list 18844 _Tech.prototype.dispose.call(this); 18845 }; 18846 18847 /** 18848 * When a captions track is enabled in the iOS Safari native player, all other 18849 * tracks are disabled (including metadata tracks), which nulls all of their 18850 * associated cue points. This will restore metadata tracks to their pre-fullscreen 18851 * state in those cases so that cue points are not needlessly lost. 18852 * 18853 * @private 18854 */ 18855 18856 18857 Html5.prototype.restoreMetadataTracksInIOSNativePlayer_ = function restoreMetadataTracksInIOSNativePlayer_() { 18858 var textTracks = this.textTracks(); 18859 var metadataTracksPreFullscreenState = void 0; 18860 18861 // captures a snapshot of every metadata track's current state 18862 var takeMetadataTrackSnapshot = function takeMetadataTrackSnapshot() { 18863 metadataTracksPreFullscreenState = []; 18864 18865 for (var i = 0; i < textTracks.length; i++) { 18866 var track = textTracks[i]; 18867 18868 if (track.kind === 'metadata') { 18869 metadataTracksPreFullscreenState.push({ 18870 track: track, 18871 storedMode: track.mode 18872 }); 18873 } 18874 } 18875 }; 18876 18877 // snapshot each metadata track's initial state, and update the snapshot 18878 // each time there is a track 'change' event 18879 takeMetadataTrackSnapshot(); 18880 textTracks.addEventListener('change', takeMetadataTrackSnapshot); 18881 18882 this.on('dispose', function () { 18883 return textTracks.removeEventListener('change', takeMetadataTrackSnapshot); 18884 }); 18885 18886 var restoreTrackMode = function restoreTrackMode() { 18887 for (var i = 0; i < metadataTracksPreFullscreenState.length; i++) { 18888 var storedTrack = metadataTracksPreFullscreenState[i]; 18889 18890 if (storedTrack.track.mode === 'disabled' && storedTrack.track.mode !== storedTrack.storedMode) { 18891 storedTrack.track.mode = storedTrack.storedMode; 18892 } 18893 } 18894 // we only want this handler to be executed on the first 'change' event 18895 textTracks.removeEventListener('change', restoreTrackMode); 18896 }; 18897 18898 // when we enter fullscreen playback, stop updating the snapshot and 18899 // restore all track modes to their pre-fullscreen state 18900 this.on('webkitbeginfullscreen', function () { 18901 textTracks.removeEventListener('change', takeMetadataTrackSnapshot); 18902 18903 // remove the listener before adding it just in case it wasn't previously removed 18904 textTracks.removeEventListener('change', restoreTrackMode); 18905 textTracks.addEventListener('change', restoreTrackMode); 18906 }); 18907 18908 // start updating the snapshot again after leaving fullscreen 18909 this.on('webkitendfullscreen', function () { 18910 // remove the listener before adding it just in case it wasn't previously removed 18911 textTracks.removeEventListener('change', takeMetadataTrackSnapshot); 18912 textTracks.addEventListener('change', takeMetadataTrackSnapshot); 18913 18914 // remove the restoreTrackMode handler in case it wasn't triggered during fullscreen playback 18915 textTracks.removeEventListener('change', restoreTrackMode); 18916 }); 18917 }; 18918 18919 /** 18920 * Proxy all native track list events to our track lists if the browser we are playing 18921 * in supports that type of track list. 18922 * 18923 * @private 18924 */ 18925 18926 18927 Html5.prototype.proxyNativeTracks_ = function proxyNativeTracks_() { 18928 var _this2 = this; 18929 18930 NORMAL.names.forEach(function (name) { 18931 var props = NORMAL[name]; 18932 var elTracks = _this2.el()[props.getterName]; 18933 var techTracks = _this2[props.getterName](); 18934 18935 if (!_this2['featuresNative' + props.capitalName + 'Tracks'] || !elTracks || !elTracks.addEventListener) { 18936 return; 18937 } 18938 var listeners = { 18939 change: function change(e) { 18940 techTracks.trigger({ 18941 type: 'change', 18942 target: techTracks, 18943 currentTarget: techTracks, 18944 srcElement: techTracks 18945 }); 18946 }, 18947 addtrack: function addtrack(e) { 18948 techTracks.addTrack(e.track); 18949 }, 18950 removetrack: function removetrack(e) { 18951 techTracks.removeTrack(e.track); 18952 } 18953 }; 18954 var removeOldTracks = function removeOldTracks() { 18955 var removeTracks = []; 18956 18957 for (var i = 0; i < techTracks.length; i++) { 18958 var found = false; 18959 18960 for (var j = 0; j < elTracks.length; j++) { 18961 if (elTracks[j] === techTracks[i]) { 18962 found = true; 18963 break; 18964 } 18965 } 18966 18967 if (!found) { 18968 removeTracks.push(techTracks[i]); 18969 } 18970 } 18971 18972 while (removeTracks.length) { 18973 techTracks.removeTrack(removeTracks.shift()); 18974 } 18975 }; 18976 18977 Object.keys(listeners).forEach(function (eventName) { 18978 var listener = listeners[eventName]; 18979 18980 elTracks.addEventListener(eventName, listener); 18981 _this2.on('dispose', function (e) { 18982 return elTracks.removeEventListener(eventName, listener); 18983 }); 18984 }); 18985 18986 // Remove (native) tracks that are not used anymore 18987 _this2.on('loadstart', removeOldTracks); 18988 _this2.on('dispose', function (e) { 18989 return _this2.off('loadstart', removeOldTracks); 18990 }); 18991 }); 18992 }; 18993 18994 /** 18995 * Create the `Html5` Tech's DOM element. 18996 * 18997 * @return {Element} 18998 * The element that gets created. 18999 */ 19000 19001 19002 Html5.prototype.createEl = function createEl$$1() { 19003 var el = this.options_.tag; 19004 19005 // Check if this browser supports moving the element into the box. 19006 // On the iPhone video will break if you move the element, 19007 // So we have to create a brand new element. 19008 // If we ingested the player div, we do not need to move the media element. 19009 if (!el || !(this.options_.playerElIngest || this.movingMediaElementInDOM)) { 19010 19011 // If the original tag is still there, clone and remove it. 19012 if (el) { 19013 var clone = el.cloneNode(true); 19014 19015 if (el.parentNode) { 19016 el.parentNode.insertBefore(clone, el); 19017 } 19018 Html5.disposeMediaElement(el); 19019 el = clone; 19020 } else { 19021 el = document_1.createElement('video'); 19022 19023 // determine if native controls should be used 19024 var tagAttributes = this.options_.tag && getAttributes(this.options_.tag); 19025 var attributes = mergeOptions({}, tagAttributes); 19026 19027 if (!TOUCH_ENABLED || this.options_.nativeControlsForTouch !== true) { 19028 delete attributes.controls; 19029 } 19030 19031 setAttributes(el, assign(attributes, { 19032 id: this.options_.techId, 19033 'class': 'vjs-tech' 19034 })); 19035 } 19036 19037 el.playerId = this.options_.playerId; 19038 } 19039 19040 if (typeof this.options_.preload !== 'undefined') { 19041 setAttribute(el, 'preload', this.options_.preload); 19042 } 19043 19044 // Update specific tag settings, in case they were overridden 19045 // `autoplay` has to be *last* so that `muted` and `playsinline` are present 19046 // when iOS/Safari or other browsers attempt to autoplay. 19047 var settingsAttrs = ['loop', 'muted', 'playsinline', 'autoplay']; 19048 19049 for (var i = 0; i < settingsAttrs.length; i++) { 19050 var attr = settingsAttrs[i]; 19051 var value = this.options_[attr]; 19052 19053 if (typeof value !== 'undefined') { 19054 if (value) { 19055 setAttribute(el, attr, attr); 19056 } else { 19057 removeAttribute(el, attr); 19058 } 19059 el[attr] = value; 19060 } 19061 } 19062 19063 return el; 19064 }; 19065 19066 /** 19067 * This will be triggered if the loadstart event has already fired, before videojs was 19068 * ready. Two known examples of when this can happen are: 19069 * 1. If we're loading the playback object after it has started loading 19070 * 2. The media is already playing the (often with autoplay on) then 19071 * 19072 * This function will fire another loadstart so that videojs can catchup. 19073 * 19074 * @fires Tech#loadstart 19075 * 19076 * @return {undefined} 19077 * returns nothing. 19078 */ 19079 19080 19081 Html5.prototype.handleLateInit_ = function handleLateInit_(el) { 19082 if (el.networkState === 0 || el.networkState === 3) { 19083 // The video element hasn't started loading the source yet 19084 // or didn't find a source 19085 return; 19086 } 19087 19088 if (el.readyState === 0) { 19089 // NetworkState is set synchronously BUT loadstart is fired at the 19090 // end of the current stack, usually before setInterval(fn, 0). 19091 // So at this point we know loadstart may have already fired or is 19092 // about to fire, and either way the player hasn't seen it yet. 19093 // We don't want to fire loadstart prematurely here and cause a 19094 // double loadstart so we'll wait and see if it happens between now 19095 // and the next loop, and fire it if not. 19096 // HOWEVER, we also want to make sure it fires before loadedmetadata 19097 // which could also happen between now and the next loop, so we'll 19098 // watch for that also. 19099 var loadstartFired = false; 19100 var setLoadstartFired = function setLoadstartFired() { 19101 loadstartFired = true; 19102 }; 19103 19104 this.on('loadstart', setLoadstartFired); 19105 19106 var triggerLoadstart = function triggerLoadstart() { 19107 // We did miss the original loadstart. Make sure the player 19108 // sees loadstart before loadedmetadata 19109 if (!loadstartFired) { 19110 this.trigger('loadstart'); 19111 } 19112 }; 19113 19114 this.on('loadedmetadata', triggerLoadstart); 19115 19116 this.ready(function () { 19117 this.off('loadstart', setLoadstartFired); 19118 this.off('loadedmetadata', triggerLoadstart); 19119 19120 if (!loadstartFired) { 19121 // We did miss the original native loadstart. Fire it now. 19122 this.trigger('loadstart'); 19123 } 19124 }); 19125 19126 return; 19127 } 19128 19129 // From here on we know that loadstart already fired and we missed it. 19130 // The other readyState events aren't as much of a problem if we double 19131 // them, so not going to go to as much trouble as loadstart to prevent 19132 // that unless we find reason to. 19133 var eventsToTrigger = ['loadstart']; 19134 19135 // loadedmetadata: newly equal to HAVE_METADATA (1) or greater 19136 eventsToTrigger.push('loadedmetadata'); 19137 19138 // loadeddata: newly increased to HAVE_CURRENT_DATA (2) or greater 19139 if (el.readyState >= 2) { 19140 eventsToTrigger.push('loadeddata'); 19141 } 19142 19143 // canplay: newly increased to HAVE_FUTURE_DATA (3) or greater 19144 if (el.readyState >= 3) { 19145 eventsToTrigger.push('canplay'); 19146 } 19147 19148 // canplaythrough: newly equal to HAVE_ENOUGH_DATA (4) 19149 if (el.readyState >= 4) { 19150 eventsToTrigger.push('canplaythrough'); 19151 } 19152 19153 // We still need to give the player time to add event listeners 19154 this.ready(function () { 19155 eventsToTrigger.forEach(function (type) { 19156 this.trigger(type); 19157 }, this); 19158 }); 19159 }; 19160 19161 /** 19162 * Set current time for the `HTML5` tech. 19163 * 19164 * @param {number} seconds 19165 * Set the current time of the media to this. 19166 */ 19167 19168 19169 Html5.prototype.setCurrentTime = function setCurrentTime(seconds) { 19170 try { 19171 this.el_.currentTime = seconds; 19172 } catch (e) { 19173 log$1(e, 'Video is not ready. (Video.js)'); 19174 // this.warning(VideoJS.warnings.videoNotReady); 19175 } 19176 }; 19177 19178 /** 19179 * Get the current duration of the HTML5 media element. 19180 * 19181 * @return {number} 19182 * The duration of the media or 0 if there is no duration. 19183 */ 19184 19185 19186 Html5.prototype.duration = function duration() { 19187 var _this3 = this; 19188 19189 // Android Chrome will report duration as Infinity for VOD HLS until after 19190 // playback has started, which triggers the live display erroneously. 19191 // Return NaN if playback has not started and trigger a durationupdate once 19192 // the duration can be reliably known. 19193 if (this.el_.duration === Infinity && IS_ANDROID && IS_CHROME && this.el_.currentTime === 0) { 19194 // Wait for the first `timeupdate` with currentTime > 0 - there may be 19195 // several with 0 19196 var checkProgress = function checkProgress() { 19197 if (_this3.el_.currentTime > 0) { 19198 // Trigger durationchange for genuinely live video 19199 if (_this3.el_.duration === Infinity) { 19200 _this3.trigger('durationchange'); 19201 } 19202 _this3.off('timeupdate', checkProgress); 19203 } 19204 }; 19205 19206 this.on('timeupdate', checkProgress); 19207 return NaN; 19208 } 19209 return this.el_.duration || NaN; 19210 }; 19211 19212 /** 19213 * Get the current width of the HTML5 media element. 19214 * 19215 * @return {number} 19216 * The width of the HTML5 media element. 19217 */ 19218 19219 19220 Html5.prototype.width = function width() { 19221 return this.el_.offsetWidth; 19222 }; 19223 19224 /** 19225 * Get the current height of the HTML5 media element. 19226 * 19227 * @return {number} 19228 * The heigth of the HTML5 media element. 19229 */ 19230 19231 19232 Html5.prototype.height = function height() { 19233 return this.el_.offsetHeight; 19234 }; 19235 19236 /** 19237 * Proxy iOS `webkitbeginfullscreen` and `webkitendfullscreen` into 19238 * `fullscreenchange` event. 19239 * 19240 * @private 19241 * @fires fullscreenchange 19242 * @listens webkitendfullscreen 19243 * @listens webkitbeginfullscreen 19244 * @listens webkitbeginfullscreen 19245 */ 19246 19247 19248 Html5.prototype.proxyWebkitFullscreen_ = function proxyWebkitFullscreen_() { 19249 var _this4 = this; 19250 19251 if (!('webkitDisplayingFullscreen' in this.el_)) { 19252 return; 19253 } 19254 19255 var endFn = function endFn() { 19256 this.trigger('fullscreenchange', { isFullscreen: false }); 19257 }; 19258 19259 var beginFn = function beginFn() { 19260 if ('webkitPresentationMode' in this.el_ && this.el_.webkitPresentationMode !== 'picture-in-picture') { 19261 this.one('webkitendfullscreen', endFn); 19262 19263 this.trigger('fullscreenchange', { isFullscreen: true }); 19264 } 19265 }; 19266 19267 this.on('webkitbeginfullscreen', beginFn); 19268 this.on('dispose', function () { 19269 _this4.off('webkitbeginfullscreen', beginFn); 19270 _this4.off('webkitendfullscreen', endFn); 19271 }); 19272 }; 19273 19274 /** 19275 * Check if fullscreen is supported on the current playback device. 19276 * 19277 * @return {boolean} 19278 * - True if fullscreen is supported. 19279 * - False if fullscreen is not supported. 19280 */ 19281 19282 19283 Html5.prototype.supportsFullScreen = function supportsFullScreen() { 19284 if (typeof this.el_.webkitEnterFullScreen === 'function') { 19285 var userAgent = window_1.navigator && window_1.navigator.userAgent || ''; 19286 19287 // Seems to be broken in Chromium/Chrome && Safari in Leopard 19288 if (/Android/.test(userAgent) || !/Chrome|Mac OS X 10.5/.test(userAgent)) { 19289 return true; 19290 } 19291 } 19292 return false; 19293 }; 19294 19295 /** 19296 * Request that the `HTML5` Tech enter fullscreen. 19297 */ 19298 19299 19300 Html5.prototype.enterFullScreen = function enterFullScreen() { 19301 var video = this.el_; 19302 19303 if (video.paused && video.networkState <= video.HAVE_METADATA) { 19304 // attempt to prime the video element for programmatic access 19305 // this isn't necessary on the desktop but shouldn't hurt 19306 this.el_.play(); 19307 19308 // playing and pausing synchronously during the transition to fullscreen 19309 // can get iOS ~6.1 devices into a play/pause loop 19310 this.setTimeout(function () { 19311 video.pause(); 19312 video.webkitEnterFullScreen(); 19313 }, 0); 19314 } else { 19315 video.webkitEnterFullScreen(); 19316 } 19317 }; 19318 19319 /** 19320 * Request that the `HTML5` Tech exit fullscreen. 19321 */ 19322 19323 19324 Html5.prototype.exitFullScreen = function exitFullScreen() { 19325 this.el_.webkitExitFullScreen(); 19326 }; 19327 19328 /** 19329 * A getter/setter for the `Html5` Tech's source object. 19330 * > Note: Please use {@link Html5#setSource} 19331 * 19332 * @param {Tech~SourceObject} [src] 19333 * The source object you want to set on the `HTML5` techs element. 19334 * 19335 * @return {Tech~SourceObject|undefined} 19336 * - The current source object when a source is not passed in. 19337 * - undefined when setting 19338 * 19339 * @deprecated Since version 5. 19340 */ 19341 19342 19343 Html5.prototype.src = function src(_src) { 19344 if (_src === undefined) { 19345 return this.el_.src; 19346 } 19347 19348 // Setting src through `src` instead of `setSrc` will be deprecated 19349 this.setSrc(_src); 19350 }; 19351 19352 /** 19353 * Reset the tech by removing all sources and then calling 19354 * {@link Html5.resetMediaElement}. 19355 */ 19356 19357 19358 Html5.prototype.reset = function reset() { 19359 Html5.resetMediaElement(this.el_); 19360 }; 19361 19362 /** 19363 * Get the current source on the `HTML5` Tech. Falls back to returning the source from 19364 * the HTML5 media element. 19365 * 19366 * @return {Tech~SourceObject} 19367 * The current source object from the HTML5 tech. With a fallback to the 19368 * elements source. 19369 */ 19370 19371 19372 Html5.prototype.currentSrc = function currentSrc() { 19373 if (this.currentSource_) { 19374 return this.currentSource_.src; 19375 } 19376 return this.el_.currentSrc; 19377 }; 19378 19379 /** 19380 * Set controls attribute for the HTML5 media Element. 19381 * 19382 * @param {string} val 19383 * Value to set the controls attribute to 19384 */ 19385 19386 19387 Html5.prototype.setControls = function setControls(val) { 19388 this.el_.controls = !!val; 19389 }; 19390 19391 /** 19392 * Create and returns a remote {@link TextTrack} object. 19393 * 19394 * @param {string} kind 19395 * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata) 19396 * 19397 * @param {string} [label] 19398 * Label to identify the text track 19399 * 19400 * @param {string} [language] 19401 * Two letter language abbreviation 19402 * 19403 * @return {TextTrack} 19404 * The TextTrack that gets created. 19405 */ 19406 19407 19408 Html5.prototype.addTextTrack = function addTextTrack(kind, label, language) { 19409 if (!this.featuresNativeTextTracks) { 19410 return _Tech.prototype.addTextTrack.call(this, kind, label, language); 19411 } 19412 19413 return this.el_.addTextTrack(kind, label, language); 19414 }; 19415 19416 /** 19417 * Creates either native TextTrack or an emulated TextTrack depending 19418 * on the value of `featuresNativeTextTracks` 19419 * 19420 * @param {Object} options 19421 * The object should contain the options to intialize the TextTrack with. 19422 * 19423 * @param {string} [options.kind] 19424 * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata). 19425 * 19426 * @param {string} [options.label]. 19427 * Label to identify the text track 19428 * 19429 * @param {string} [options.language] 19430 * Two letter language abbreviation. 19431 * 19432 * @param {boolean} [options.default] 19433 * Default this track to on. 19434 * 19435 * @param {string} [options.id] 19436 * The internal id to assign this track. 19437 * 19438 * @param {string} [options.src] 19439 * A source url for the track. 19440 * 19441 * @return {HTMLTrackElement} 19442 * The track element that gets created. 19443 */ 19444 19445 19446 Html5.prototype.createRemoteTextTrack = function createRemoteTextTrack(options) { 19447 if (!this.featuresNativeTextTracks) { 19448 return _Tech.prototype.createRemoteTextTrack.call(this, options); 19449 } 19450 var htmlTrackElement = document_1.createElement('track'); 19451 19452 if (options.kind) { 19453 htmlTrackElement.kind = options.kind; 19454 } 19455 if (options.label) { 19456 htmlTrackElement.label = options.label; 19457 } 19458 if (options.language || options.srclang) { 19459 htmlTrackElement.srclang = options.language || options.srclang; 19460 } 19461 if (options['default']) { 19462 htmlTrackElement['default'] = options['default']; 19463 } 19464 if (options.id) { 19465 htmlTrackElement.id = options.id; 19466 } 19467 if (options.src) { 19468 htmlTrackElement.src = options.src; 19469 } 19470 19471 return htmlTrackElement; 19472 }; 19473 19474 /** 19475 * Creates a remote text track object and returns an html track element. 19476 * 19477 * @param {Object} options The object should contain values for 19478 * kind, language, label, and src (location of the WebVTT file) 19479 * @param {Boolean} [manualCleanup=true] if set to false, the TextTrack will be 19480 * automatically removed from the video element whenever the source changes 19481 * @return {HTMLTrackElement} An Html Track Element. 19482 * This can be an emulated {@link HTMLTrackElement} or a native one. 19483 * @deprecated The default value of the "manualCleanup" parameter will default 19484 * to "false" in upcoming versions of Video.js 19485 */ 19486 19487 19488 Html5.prototype.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) { 19489 var htmlTrackElement = _Tech.prototype.addRemoteTextTrack.call(this, options, manualCleanup); 19490 19491 if (this.featuresNativeTextTracks) { 19492 this.el().appendChild(htmlTrackElement); 19493 } 19494 19495 return htmlTrackElement; 19496 }; 19497 19498 /** 19499 * Remove remote `TextTrack` from `TextTrackList` object 19500 * 19501 * @param {TextTrack} track 19502 * `TextTrack` object to remove 19503 */ 19504 19505 19506 Html5.prototype.removeRemoteTextTrack = function removeRemoteTextTrack(track) { 19507 _Tech.prototype.removeRemoteTextTrack.call(this, track); 19508 19509 if (this.featuresNativeTextTracks) { 19510 var tracks = this.$$('track'); 19511 19512 var i = tracks.length; 19513 19514 while (i--) { 19515 if (track === tracks[i] || track === tracks[i].track) { 19516 this.el().removeChild(tracks[i]); 19517 } 19518 } 19519 } 19520 }; 19521 19522 /** 19523 * Gets available media playback quality metrics as specified by the W3C's Media 19524 * Playback Quality API. 19525 * 19526 * @see [Spec]{@link https://wicg.github.io/media-playback-quality} 19527 * 19528 * @return {Object} 19529 * An object with supported media playback quality metrics 19530 */ 19531 19532 19533 Html5.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() { 19534 if (typeof this.el().getVideoPlaybackQuality === 'function') { 19535 return this.el().getVideoPlaybackQuality(); 19536 } 19537 19538 var videoPlaybackQuality = {}; 19539 19540 if (typeof this.el().webkitDroppedFrameCount !== 'undefined' && typeof this.el().webkitDecodedFrameCount !== 'undefined') { 19541 videoPlaybackQuality.droppedVideoFrames = this.el().webkitDroppedFrameCount; 19542 videoPlaybackQuality.totalVideoFrames = this.el().webkitDecodedFrameCount; 19543 } 19544 19545 if (window_1.performance && typeof window_1.performance.now === 'function') { 19546 videoPlaybackQuality.creationTime = window_1.performance.now(); 19547 } else if (window_1.performance && window_1.performance.timing && typeof window_1.performance.timing.navigationStart === 'number') { 19548 videoPlaybackQuality.creationTime = window_1.Date.now() - window_1.performance.timing.navigationStart; 19549 } 19550 19551 return videoPlaybackQuality; 19552 }; 19553 19554 return Html5; 19555}(Tech); 19556 19557/* HTML5 Support Testing ---------------------------------------------------- */ 19558 19559if (isReal()) { 19560 19561 /** 19562 * Element for testing browser HTML5 media capabilities 19563 * 19564 * @type {Element} 19565 * @constant 19566 * @private 19567 */ 19568 Html5.TEST_VID = document_1.createElement('video'); 19569 var track = document_1.createElement('track'); 19570 19571 track.kind = 'captions'; 19572 track.srclang = 'en'; 19573 track.label = 'English'; 19574 Html5.TEST_VID.appendChild(track); 19575} 19576 19577/** 19578 * Check if HTML5 media is supported by this browser/device. 19579 * 19580 * @return {boolean} 19581 * - True if HTML5 media is supported. 19582 * - False if HTML5 media is not supported. 19583 */ 19584Html5.isSupported = function () { 19585 // IE9 with no Media Player is a LIAR! (#984) 19586 try { 19587 Html5.TEST_VID.volume = 0.5; 19588 } catch (e) { 19589 return false; 19590 } 19591 19592 return !!(Html5.TEST_VID && Html5.TEST_VID.canPlayType); 19593}; 19594 19595/** 19596 * Check if the tech can support the given type 19597 * 19598 * @param {string} type 19599 * The mimetype to check 19600 * @return {string} 'probably', 'maybe', or '' (empty string) 19601 */ 19602Html5.canPlayType = function (type) { 19603 return Html5.TEST_VID.canPlayType(type); 19604}; 19605 19606/** 19607 * Check if the tech can support the given source 19608 * @param {Object} srcObj 19609 * The source object 19610 * @param {Object} options 19611 * The options passed to the tech 19612 * @return {string} 'probably', 'maybe', or '' (empty string) 19613 */ 19614Html5.canPlaySource = function (srcObj, options) { 19615 return Html5.canPlayType(srcObj.type); 19616}; 19617 19618/** 19619 * Check if the volume can be changed in this browser/device. 19620 * Volume cannot be changed in a lot of mobile devices. 19621 * Specifically, it can't be changed from 1 on iOS. 19622 * 19623 * @return {boolean} 19624 * - True if volume can be controlled 19625 * - False otherwise 19626 */ 19627Html5.canControlVolume = function () { 19628 // IE will error if Windows Media Player not installed #3315 19629 try { 19630 var volume = Html5.TEST_VID.volume; 19631 19632 Html5.TEST_VID.volume = volume / 2 + 0.1; 19633 return volume !== Html5.TEST_VID.volume; 19634 } catch (e) { 19635 return false; 19636 } 19637}; 19638 19639/** 19640 * Check if the playback rate can be changed in this browser/device. 19641 * 19642 * @return {boolean} 19643 * - True if playback rate can be controlled 19644 * - False otherwise 19645 */ 19646Html5.canControlPlaybackRate = function () { 19647 // Playback rate API is implemented in Android Chrome, but doesn't do anything 19648 // https://github.com/videojs/video.js/issues/3180 19649 if (IS_ANDROID && IS_CHROME && CHROME_VERSION < 58) { 19650 return false; 19651 } 19652 // IE will error if Windows Media Player not installed #3315 19653 try { 19654 var playbackRate = Html5.TEST_VID.playbackRate; 19655 19656 Html5.TEST_VID.playbackRate = playbackRate / 2 + 0.1; 19657 return playbackRate !== Html5.TEST_VID.playbackRate; 19658 } catch (e) { 19659 return false; 19660 } 19661}; 19662 19663/** 19664 * Check to see if native `TextTrack`s are supported by this browser/device. 19665 * 19666 * @return {boolean} 19667 * - True if native `TextTrack`s are supported. 19668 * - False otherwise 19669 */ 19670Html5.supportsNativeTextTracks = function () { 19671 return IS_ANY_SAFARI; 19672}; 19673 19674/** 19675 * Check to see if native `VideoTrack`s are supported by this browser/device 19676 * 19677 * @return {boolean} 19678 * - True if native `VideoTrack`s are supported. 19679 * - False otherwise 19680 */ 19681Html5.supportsNativeVideoTracks = function () { 19682 return !!(Html5.TEST_VID && Html5.TEST_VID.videoTracks); 19683}; 19684 19685/** 19686 * Check to see if native `AudioTrack`s are supported by this browser/device 19687 * 19688 * @return {boolean} 19689 * - True if native `AudioTrack`s are supported. 19690 * - False otherwise 19691 */ 19692Html5.supportsNativeAudioTracks = function () { 19693 return !!(Html5.TEST_VID && Html5.TEST_VID.audioTracks); 19694}; 19695 19696/** 19697 * An array of events available on the Html5 tech. 19698 * 19699 * @private 19700 * @type {Array} 19701 */ 19702Html5.Events = ['loadstart', 'suspend', 'abort', 'error', 'emptied', 'stalled', 'loadedmetadata', 'loadeddata', 'canplay', 'canplaythrough', 'playing', 'waiting', 'seeking', 'seeked', 'ended', 'durationchange', 'timeupdate', 'progress', 'play', 'pause', 'ratechange', 'resize', 'volumechange']; 19703 19704/** 19705 * Boolean indicating whether the `Tech` supports volume control. 19706 * 19707 * @type {boolean} 19708 * @default {@link Html5.canControlVolume} 19709 */ 19710Html5.prototype.featuresVolumeControl = Html5.canControlVolume(); 19711 19712/** 19713 * Boolean indicating whether the `Tech` supports changing the speed at which the media 19714 * plays. Examples: 19715 * - Set player to play 2x (twice) as fast 19716 * - Set player to play 0.5x (half) as fast 19717 * 19718 * @type {boolean} 19719 * @default {@link Html5.canControlPlaybackRate} 19720 */ 19721Html5.prototype.featuresPlaybackRate = Html5.canControlPlaybackRate(); 19722 19723/** 19724 * Boolean indicating whether the `HTML5` tech currently supports the media element 19725 * moving in the DOM. iOS breaks if you move the media element, so this is set this to 19726 * false there. Everywhere else this should be true. 19727 * 19728 * @type {boolean} 19729 * @default 19730 */ 19731Html5.prototype.movingMediaElementInDOM = !IS_IOS; 19732 19733// TODO: Previous comment: No longer appears to be used. Can probably be removed. 19734// Is this true? 19735/** 19736 * Boolean indicating whether the `HTML5` tech currently supports automatic media resize 19737 * when going into fullscreen. 19738 * 19739 * @type {boolean} 19740 * @default 19741 */ 19742Html5.prototype.featuresFullscreenResize = true; 19743 19744/** 19745 * Boolean indicating whether the `HTML5` tech currently supports the progress event. 19746 * If this is false, manual `progress` events will be triggred instead. 19747 * 19748 * @type {boolean} 19749 * @default 19750 */ 19751Html5.prototype.featuresProgressEvents = true; 19752 19753/** 19754 * Boolean indicating whether the `HTML5` tech currently supports the timeupdate event. 19755 * If this is false, manual `timeupdate` events will be triggred instead. 19756 * 19757 * @default 19758 */ 19759Html5.prototype.featuresTimeupdateEvents = true; 19760 19761/** 19762 * Boolean indicating whether the `HTML5` tech currently supports native `TextTrack`s. 19763 * 19764 * @type {boolean} 19765 * @default {@link Html5.supportsNativeTextTracks} 19766 */ 19767Html5.prototype.featuresNativeTextTracks = Html5.supportsNativeTextTracks(); 19768 19769/** 19770 * Boolean indicating whether the `HTML5` tech currently supports native `VideoTrack`s. 19771 * 19772 * @type {boolean} 19773 * @default {@link Html5.supportsNativeVideoTracks} 19774 */ 19775Html5.prototype.featuresNativeVideoTracks = Html5.supportsNativeVideoTracks(); 19776 19777/** 19778 * Boolean indicating whether the `HTML5` tech currently supports native `AudioTrack`s. 19779 * 19780 * @type {boolean} 19781 * @default {@link Html5.supportsNativeAudioTracks} 19782 */ 19783Html5.prototype.featuresNativeAudioTracks = Html5.supportsNativeAudioTracks(); 19784 19785// HTML5 Feature detection and Device Fixes --------------------------------- // 19786var canPlayType = Html5.TEST_VID && Html5.TEST_VID.constructor.prototype.canPlayType; 19787var mpegurlRE = /^application\/(?:x-|vnd\.apple\.)mpegurl/i; 19788var mp4RE = /^video\/mp4/i; 19789 19790Html5.patchCanPlayType = function () { 19791 19792 // Android 4.0 and above can play HLS to some extent but it reports being unable to do so 19793 if (ANDROID_VERSION >= 4.0 && !IS_FIREFOX) { 19794 Html5.TEST_VID.constructor.prototype.canPlayType = function (type) { 19795 if (type && mpegurlRE.test(type)) { 19796 return 'maybe'; 19797 } 19798 return canPlayType.call(this, type); 19799 }; 19800 19801 // Override Android 2.2 and less canPlayType method which is broken 19802 } else if (IS_OLD_ANDROID) { 19803 Html5.TEST_VID.constructor.prototype.canPlayType = function (type) { 19804 if (type && mp4RE.test(type)) { 19805 return 'maybe'; 19806 } 19807 return canPlayType.call(this, type); 19808 }; 19809 } 19810}; 19811 19812Html5.unpatchCanPlayType = function () { 19813 var r = Html5.TEST_VID.constructor.prototype.canPlayType; 19814 19815 Html5.TEST_VID.constructor.prototype.canPlayType = canPlayType; 19816 return r; 19817}; 19818 19819// by default, patch the media element 19820Html5.patchCanPlayType(); 19821 19822Html5.disposeMediaElement = function (el) { 19823 if (!el) { 19824 return; 19825 } 19826 19827 if (el.parentNode) { 19828 el.parentNode.removeChild(el); 19829 } 19830 19831 // remove any child track or source nodes to prevent their loading 19832 while (el.hasChildNodes()) { 19833 el.removeChild(el.firstChild); 19834 } 19835 19836 // remove any src reference. not setting `src=''` because that causes a warning 19837 // in firefox 19838 el.removeAttribute('src'); 19839 19840 // force the media element to update its loading state by calling load() 19841 // however IE on Windows 7N has a bug that throws an error so need a try/catch (#793) 19842 if (typeof el.load === 'function') { 19843 // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473) 19844 (function () { 19845 try { 19846 el.load(); 19847 } catch (e) { 19848 // not supported 19849 } 19850 })(); 19851 } 19852}; 19853 19854Html5.resetMediaElement = function (el) { 19855 if (!el) { 19856 return; 19857 } 19858 19859 var sources = el.querySelectorAll('source'); 19860 var i = sources.length; 19861 19862 while (i--) { 19863 el.removeChild(sources[i]); 19864 } 19865 19866 // remove any src reference. 19867 // not setting `src=''` because that throws an error 19868 el.removeAttribute('src'); 19869 19870 if (typeof el.load === 'function') { 19871 // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473) 19872 (function () { 19873 try { 19874 el.load(); 19875 } catch (e) { 19876 // satisfy linter 19877 } 19878 })(); 19879 } 19880}; 19881 19882/* Native HTML5 element property wrapping ----------------------------------- */ 19883// Wrap native boolean attributes with getters that check both property and attribute 19884// The list is as followed: 19885// muted, defaultMuted, autoplay, controls, loop, playsinline 19886[ 19887/** 19888 * Get the value of `muted` from the media element. `muted` indicates 19889 * that the volume for the media should be set to silent. This does not actually change 19890 * the `volume` attribute. 19891 * 19892 * @method Html5#muted 19893 * @return {boolean} 19894 * - True if the value of `volume` should be ignored and the audio set to silent. 19895 * - False if the value of `volume` should be used. 19896 * 19897 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted} 19898 */ 19899'muted', 19900 19901/** 19902 * Get the value of `defaultMuted` from the media element. `defaultMuted` indicates 19903 * whether the media should start muted or not. Only changes the default state of the 19904 * media. `muted` and `defaultMuted` can have different values. {@link Html5#muted} indicates the 19905 * current state. 19906 * 19907 * @method Html5#defaultMuted 19908 * @return {boolean} 19909 * - The value of `defaultMuted` from the media element. 19910 * - True indicates that the media should start muted. 19911 * - False indicates that the media should not start muted 19912 * 19913 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted} 19914 */ 19915'defaultMuted', 19916 19917/** 19918 * Get the value of `autoplay` from the media element. `autoplay` indicates 19919 * that the media should start to play as soon as the page is ready. 19920 * 19921 * @method Html5#autoplay 19922 * @return {boolean} 19923 * - The value of `autoplay` from the media element. 19924 * - True indicates that the media should start as soon as the page loads. 19925 * - False indicates that the media should not start as soon as the page loads. 19926 * 19927 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay} 19928 */ 19929'autoplay', 19930 19931/** 19932 * Get the value of `controls` from the media element. `controls` indicates 19933 * whether the native media controls should be shown or hidden. 19934 * 19935 * @method Html5#controls 19936 * @return {boolean} 19937 * - The value of `controls` from the media element. 19938 * - True indicates that native controls should be showing. 19939 * - False indicates that native controls should be hidden. 19940 * 19941 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-controls} 19942 */ 19943'controls', 19944 19945/** 19946 * Get the value of `loop` from the media element. `loop` indicates 19947 * that the media should return to the start of the media and continue playing once 19948 * it reaches the end. 19949 * 19950 * @method Html5#loop 19951 * @return {boolean} 19952 * - The value of `loop` from the media element. 19953 * - True indicates that playback should seek back to start once 19954 * the end of a media is reached. 19955 * - False indicates that playback should not loop back to the start when the 19956 * end of the media is reached. 19957 * 19958 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop} 19959 */ 19960'loop', 19961 19962/** 19963 * Get the value of `playsinline` from the media element. `playsinline` indicates 19964 * to the browser that non-fullscreen playback is preferred when fullscreen 19965 * playback is the native default, such as in iOS Safari. 19966 * 19967 * @method Html5#playsinline 19968 * @return {boolean} 19969 * - The value of `playsinline` from the media element. 19970 * - True indicates that the media should play inline. 19971 * - False indicates that the media should not play inline. 19972 * 19973 * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline} 19974 */ 19975'playsinline'].forEach(function (prop) { 19976 Html5.prototype[prop] = function () { 19977 return this.el_[prop] || this.el_.hasAttribute(prop); 19978 }; 19979}); 19980 19981// Wrap native boolean attributes with setters that set both property and attribute 19982// The list is as followed: 19983// setMuted, setDefaultMuted, setAutoplay, setLoop, setPlaysinline 19984// setControls is special-cased above 19985[ 19986/** 19987 * Set the value of `muted` on the media element. `muted` indicates that the current 19988 * audio level should be silent. 19989 * 19990 * @method Html5#setMuted 19991 * @param {boolean} muted 19992 * - True if the audio should be set to silent 19993 * - False otherwise 19994 * 19995 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted} 19996 */ 19997'muted', 19998 19999/** 20000 * Set the value of `defaultMuted` on the media element. `defaultMuted` indicates that the current 20001 * audio level should be silent, but will only effect the muted level on intial playback.. 20002 * 20003 * @method Html5.prototype.setDefaultMuted 20004 * @param {boolean} defaultMuted 20005 * - True if the audio should be set to silent 20006 * - False otherwise 20007 * 20008 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted} 20009 */ 20010'defaultMuted', 20011 20012/** 20013 * Set the value of `autoplay` on the media element. `autoplay` indicates 20014 * that the media should start to play as soon as the page is ready. 20015 * 20016 * @method Html5#setAutoplay 20017 * @param {boolean} autoplay 20018 * - True indicates that the media should start as soon as the page loads. 20019 * - False indicates that the media should not start as soon as the page loads. 20020 * 20021 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay} 20022 */ 20023'autoplay', 20024 20025/** 20026 * Set the value of `loop` on the media element. `loop` indicates 20027 * that the media should return to the start of the media and continue playing once 20028 * it reaches the end. 20029 * 20030 * @method Html5#setLoop 20031 * @param {boolean} loop 20032 * - True indicates that playback should seek back to start once 20033 * the end of a media is reached. 20034 * - False indicates that playback should not loop back to the start when the 20035 * end of the media is reached. 20036 * 20037 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop} 20038 */ 20039'loop', 20040 20041/** 20042 * Set the value of `playsinline` from the media element. `playsinline` indicates 20043 * to the browser that non-fullscreen playback is preferred when fullscreen 20044 * playback is the native default, such as in iOS Safari. 20045 * 20046 * @method Html5#setPlaysinline 20047 * @param {boolean} playsinline 20048 * - True indicates that the media should play inline. 20049 * - False indicates that the media should not play inline. 20050 * 20051 * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline} 20052 */ 20053'playsinline'].forEach(function (prop) { 20054 Html5.prototype['set' + toTitleCase(prop)] = function (v) { 20055 this.el_[prop] = v; 20056 20057 if (v) { 20058 this.el_.setAttribute(prop, prop); 20059 } else { 20060 this.el_.removeAttribute(prop); 20061 } 20062 }; 20063}); 20064 20065// Wrap native properties with a getter 20066// The list is as followed 20067// paused, currentTime, buffered, volume, poster, preload, error, seeking 20068// seekable, ended, playbackRate, defaultPlaybackRate, played, networkState 20069// readyState, videoWidth, videoHeight 20070[ 20071/** 20072 * Get the value of `paused` from the media element. `paused` indicates whether the media element 20073 * is currently paused or not. 20074 * 20075 * @method Html5#paused 20076 * @return {boolean} 20077 * The value of `paused` from the media element. 20078 * 20079 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-paused} 20080 */ 20081'paused', 20082 20083/** 20084 * Get the value of `currentTime` from the media element. `currentTime` indicates 20085 * the current second that the media is at in playback. 20086 * 20087 * @method Html5#currentTime 20088 * @return {number} 20089 * The value of `currentTime` from the media element. 20090 * 20091 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-currenttime} 20092 */ 20093'currentTime', 20094 20095/** 20096 * Get the value of `buffered` from the media element. `buffered` is a `TimeRange` 20097 * object that represents the parts of the media that are already downloaded and 20098 * available for playback. 20099 * 20100 * @method Html5#buffered 20101 * @return {TimeRange} 20102 * The value of `buffered` from the media element. 20103 * 20104 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-buffered} 20105 */ 20106'buffered', 20107 20108/** 20109 * Get the value of `volume` from the media element. `volume` indicates 20110 * the current playback volume of audio for a media. `volume` will be a value from 0 20111 * (silent) to 1 (loudest and default). 20112 * 20113 * @method Html5#volume 20114 * @return {number} 20115 * The value of `volume` from the media element. Value will be between 0-1. 20116 * 20117 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume} 20118 */ 20119'volume', 20120 20121/** 20122 * Get the value of `poster` from the media element. `poster` indicates 20123 * that the url of an image file that can/will be shown when no media data is available. 20124 * 20125 * @method Html5#poster 20126 * @return {string} 20127 * The value of `poster` from the media element. Value will be a url to an 20128 * image. 20129 * 20130 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-video-poster} 20131 */ 20132'poster', 20133 20134/** 20135 * Get the value of `preload` from the media element. `preload` indicates 20136 * what should download before the media is interacted with. It can have the following 20137 * values: 20138 * - none: nothing should be downloaded 20139 * - metadata: poster and the first few frames of the media may be downloaded to get 20140 * media dimensions and other metadata 20141 * - auto: allow the media and metadata for the media to be downloaded before 20142 * interaction 20143 * 20144 * @method Html5#preload 20145 * @return {string} 20146 * The value of `preload` from the media element. Will be 'none', 'metadata', 20147 * or 'auto'. 20148 * 20149 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload} 20150 */ 20151'preload', 20152 20153/** 20154 * Get the value of the `error` from the media element. `error` indicates any 20155 * MediaError that may have occured during playback. If error returns null there is no 20156 * current error. 20157 * 20158 * @method Html5#error 20159 * @return {MediaError|null} 20160 * The value of `error` from the media element. Will be `MediaError` if there 20161 * is a current error and null otherwise. 20162 * 20163 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-error} 20164 */ 20165'error', 20166 20167/** 20168 * Get the value of `seeking` from the media element. `seeking` indicates whether the 20169 * media is currently seeking to a new position or not. 20170 * 20171 * @method Html5#seeking 20172 * @return {boolean} 20173 * - The value of `seeking` from the media element. 20174 * - True indicates that the media is currently seeking to a new position. 20175 * - Flase indicates that the media is not seeking to a new position at this time. 20176 * 20177 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seeking} 20178 */ 20179'seeking', 20180 20181/** 20182 * Get the value of `seekable` from the media element. `seekable` returns a 20183 * `TimeRange` object indicating ranges of time that can currently be `seeked` to. 20184 * 20185 * @method Html5#seekable 20186 * @return {TimeRange} 20187 * The value of `seekable` from the media element. A `TimeRange` object 20188 * indicating the current ranges of time that can be seeked to. 20189 * 20190 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seekable} 20191 */ 20192'seekable', 20193 20194/** 20195 * Get the value of `ended` from the media element. `ended` indicates whether 20196 * the media has reached the end or not. 20197 * 20198 * @method Html5#ended 20199 * @return {boolean} 20200 * - The value of `ended` from the media element. 20201 * - True indicates that the media has ended. 20202 * - False indicates that the media has not ended. 20203 * 20204 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-ended} 20205 */ 20206'ended', 20207 20208/** 20209 * Get the value of `playbackRate` from the media element. `playbackRate` indicates
20210 * the rate at which the media is currently playing back. Examples: 20211 * - if playbackRate is set to 2, media will play twice as fast. 20212 * - if playbackRate is set to 0.5, media will play half as fast. 20213 * 20214 * @method Html5#playbackRate 20215 * @return {number} 20216 * The value of `playbackRate` from the media element. A number indicating 20217 * the current playback speed of the media, where 1 is normal speed. 20218 * 20219 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate} 20220 */ 20221'playbackRate', 20222 20223/** 20224 * Get the value of `defaultPlaybackRate` from the media element. `defaultPlaybackRate` indicates 20225 * the rate at which the media is currently playing back. This value will not indicate the current 20226 * `playbackRate` after playback has started, use {@link Html5#playbackRate} for that. 20227 * 20228 * Examples: 20229 * - if defaultPlaybackRate is set to 2, media will play twice as fast. 20230 * - if defaultPlaybackRate is set to 0.5, media will play half as fast. 20231 * 20232 * @method Html5.prototype.defaultPlaybackRate 20233 * @return {number} 20234 * The value of `defaultPlaybackRate` from the media element. A number indicating 20235 * the current playback speed of the media, where 1 is normal speed. 20236 * 20237 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate} 20238 */ 20239'defaultPlaybackRate', 20240 20241/** 20242 * Get the value of `played` from the media element. `played` returns a `TimeRange` 20243 * object representing points in the media timeline that have been played. 20244 * 20245 * @method Html5#played 20246 * @return {TimeRange} 20247 * The value of `played` from the media element. A `TimeRange` object indicating 20248 * the ranges of time that have been played. 20249 * 20250 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-played} 20251 */ 20252'played', 20253 20254/** 20255 * Get the value of `networkState` from the media element. `networkState` indicates 20256 * the current network state. It returns an enumeration from the following list: 20257 * - 0: NETWORK_EMPTY 20258 * - 1: NEWORK_IDLE 20259 * - 2: NETWORK_LOADING 20260 * - 3: NETWORK_NO_SOURCE 20261 * 20262 * @method Html5#networkState 20263 * @return {number} 20264 * The value of `networkState` from the media element. This will be a number 20265 * from the list in the description. 20266 * 20267 * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-networkstate} 20268 */ 20269'networkState', 20270 20271/** 20272 * Get the value of `readyState` from the media element. `readyState` indicates 20273 * the current state of the media element. It returns an enumeration from the 20274 * following list: 20275 * - 0: HAVE_NOTHING 20276 * - 1: HAVE_METADATA 20277 * - 2: HAVE_CURRENT_DATA 20278 * - 3: HAVE_FUTURE_DATA 20279 * - 4: HAVE_ENOUGH_DATA 20280 * 20281 * @method Html5#readyState 20282 * @return {number} 20283 * The value of `readyState` from the media element. This will be a number 20284 * from the list in the description. 20285 * 20286 * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#ready-states} 20287 */ 20288'readyState', 20289 20290/** 20291 * Get the value of `videoWidth` from the video element. `videoWidth` indicates 20292 * the current width of the video in css pixels. 20293 * 20294 * @method Html5#videoWidth 20295 * @return {number} 20296 * The value of `videoWidth` from the video element. This will be a number 20297 * in css pixels. 20298 * 20299 * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth} 20300 */ 20301'videoWidth', 20302 20303/** 20304 * Get the value of `videoHeight` from the video element. `videoHeigth` indicates 20305 * the current height of the video in css pixels. 20306 * 20307 * @method Html5#videoHeight 20308 * @return {number} 20309 * The value of `videoHeight` from the video element. This will be a number 20310 * in css pixels. 20311 * 20312 * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth} 20313 */ 20314'videoHeight'].forEach(function (prop) { 20315 Html5.prototype[prop] = function () { 20316 return this.el_[prop]; 20317 }; 20318}); 20319 20320// Wrap native properties with a setter in this format: 20321// set + toTitleCase(name) 20322// The list is as follows: 20323// setVolume, setSrc, setPoster, setPreload, setPlaybackRate, setDefaultPlaybackRate 20324[ 20325/** 20326 * Set the value of `volume` on the media element. `volume` indicates the current 20327 * audio level as a percentage in decimal form. This means that 1 is 100%, 0.5 is 50%, and 20328 * so on. 20329 * 20330 * @method Html5#setVolume 20331 * @param {number} percentAsDecimal 20332 * The volume percent as a decimal. Valid range is from 0-1. 20333 * 20334 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume} 20335 */ 20336'volume', 20337 20338/** 20339 * Set the value of `src` on the media element. `src` indicates the current 20340 * {@link Tech~SourceObject} for the media. 20341 * 20342 * @method Html5#setSrc 20343 * @param {Tech~SourceObject} src 20344 * The source object to set as the current source. 20345 * 20346 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-src} 20347 */ 20348'src', 20349 20350/** 20351 * Set the value of `poster` on the media element. `poster` is the url to 20352 * an image file that can/will be shown when no media data is available. 20353 * 20354 * @method Html5#setPoster 20355 * @param {string} poster 20356 * The url to an image that should be used as the `poster` for the media 20357 * element. 20358 * 20359 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-poster} 20360 */ 20361'poster', 20362 20363/** 20364 * Set the value of `preload` on the media element. `preload` indicates 20365 * what should download before the media is interacted with. It can have the following 20366 * values: 20367 * - none: nothing should be downloaded 20368 * - metadata: poster and the first few frames of the media may be downloaded to get 20369 * media dimensions and other metadata 20370 * - auto: allow the media and metadata for the media to be downloaded before 20371 * interaction 20372 * 20373 * @method Html5#setPreload 20374 * @param {string} preload 20375 * The value of `preload` to set on the media element. Must be 'none', 'metadata', 20376 * or 'auto'. 20377 * 20378 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload} 20379 */ 20380'preload', 20381 20382/** 20383 * Set the value of `playbackRate` on the media element. `playbackRate` indicates 20384 * the rate at which the media should play back. Examples: 20385 * - if playbackRate is set to 2, media will play twice as fast. 20386 * - if playbackRate is set to 0.5, media will play half as fast. 20387 * 20388 * @method Html5#setPlaybackRate 20389 * @return {number} 20390 * The value of `playbackRate` from the media element. A number indicating 20391 * the current playback speed of the media, where 1 is normal speed. 20392 * 20393 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate} 20394 */ 20395'playbackRate', 20396 20397/** 20398 * Set the value of `defaultPlaybackRate` on the media element. `defaultPlaybackRate` indicates 20399 * the rate at which the media should play back upon initial startup. Changing this value 20400 * after a video has started will do nothing. Instead you should used {@link Html5#setPlaybackRate}. 20401 * 20402 * Example Values: 20403 * - if playbackRate is set to 2, media will play twice as fast. 20404 * - if playbackRate is set to 0.5, media will play half as fast. 20405 * 20406 * @method Html5.prototype.setDefaultPlaybackRate 20407 * @return {number} 20408 * The value of `defaultPlaybackRate` from the media element. A number indicating 20409 * the current playback speed of the media, where 1 is normal speed. 20410 * 20411 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultplaybackrate} 20412 */ 20413'defaultPlaybackRate'].forEach(function (prop) { 20414 Html5.prototype['set' + toTitleCase(prop)] = function (v) { 20415 this.el_[prop] = v; 20416 }; 20417}); 20418 20419// wrap native functions with a function 20420// The list is as follows: 20421// pause, load play 20422[ 20423/** 20424 * A wrapper around the media elements `pause` function. This will call the `HTML5` 20425 * media elements `pause` function. 20426 * 20427 * @method Html5#pause 20428 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-pause} 20429 */ 20430'pause', 20431 20432/** 20433 * A wrapper around the media elements `load` function. This will call the `HTML5`s 20434 * media element `load` function. 20435 * 20436 * @method Html5#load 20437 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-load} 20438 */ 20439'load', 20440 20441/** 20442 * A wrapper around the media elements `play` function. This will call the `HTML5`s 20443 * media element `play` function. 20444 * 20445 * @method Html5#play 20446 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-play} 20447 */ 20448'play'].forEach(function (prop) { 20449 Html5.prototype[prop] = function () { 20450 return this.el_[prop](); 20451 }; 20452}); 20453 20454Tech.withSourceHandlers(Html5); 20455 20456/** 20457 * Native source handler for Html5, simply passes the source to the media element. 20458 * 20459 * @proprety {Tech~SourceObject} source 20460 * The source object 20461 * 20462 * @proprety {Html5} tech 20463 * The instance of the HTML5 tech. 20464 */ 20465Html5.nativeSourceHandler = {}; 20466 20467/** 20468 * Check if the media element can play the given mime type. 20469 * 20470 * @param {string} type 20471 * The mimetype to check 20472 * 20473 * @return {string} 20474 * 'probably', 'maybe', or '' (empty string) 20475 */ 20476Html5.nativeSourceHandler.canPlayType = function (type) { 20477 // IE9 on Windows 7 without MediaPlayer throws an error here 20478 // https://github.com/videojs/video.js/issues/519 20479 try {
20480 return Html5.TEST_VID.canPlayType(type); 20481 } catch (e) { 20482 return ''; 20483 } 20484}; 20485 20486/** 20487 * Check if the media element can handle a source natively. 20488 * 20489 * @param {Tech~SourceObject} source 20490 * The source object 20491 * 20492 * @param {Object} [options] 20493 * Options to be passed to the tech. 20494 * 20495 * @return {string} 20496 * 'probably', 'maybe', or '' (empty string). 20497 */ 20498Html5.nativeSourceHandler.canHandleSource = function (source, options) { 20499 20500 // If a type was provided we should rely on that 20501 if (source.type) { 20502 return Html5.nativeSourceHandler.canPlayType(source.type); 20503 20504 // If no type, fall back to checking 'video/[EXTENSION]' 20505 } else if (source.src) { 20506 var ext = getFileExtension(source.src); 20507 20508 return Html5.nativeSourceHandler.canPlayType('video/' + ext); 20509 } 20510 20511 return ''; 20512}; 20513 20514/** 20515 * Pass the source to the native media element. 20516 * 20517 * @param {Tech~SourceObject} source 20518 * The source object 20519 * 20520 * @param {Html5} tech 20521 * The instance of the Html5 tech 20522 * 20523 * @param {Object} [options] 20524 * The options to pass to the source 20525 */ 20526Html5.nativeSourceHandler.handleSource = function (source, tech, options) { 20527 tech.setSrc(source.src); 20528}; 20529 20530/** 20531 * A noop for the native dispose function, as cleanup is not needed. 20532 */ 20533Html5.nativeSourceHandler.dispose = function () {}; 20534 20535// Register the native source handler 20536Html5.registerSourceHandler(Html5.nativeSourceHandler); 20537 20538Tech.registerTech('Html5', Html5); 20539 20540var _templateObject$1 = taggedTemplateLiteralLoose(['\n Using the tech directly can be dangerous. I hope you know
20540what you\'re doing.\n See https://github.com/videojs/video.js/issues/2617 for more info.\n '], ['\n Using the tech directly can be dangerous. I hope you know what you\'re doing.\n See https://github.com/videojs/video.js/issues/2617 for more info.\n ']); 20541 20542/** 20543 * @file player.js 20544 */ 20545// Subclasses Component 20546// The following imports are used only to ensure that the corresponding modules 20547// are always included in the video.js package. Importing the modules will 20548// execute them and they will register themselves with video.js. 20549// Import Html5 tech, at least for disposing the original video tag. 20550// The following tech events are simply re-triggered 20551// on the player when they happen 20552var TECH_EVENTS_RETRIGGER = [ 20553/** 20554 * Fired while the user agent is downloading media data. 20555 * 20556 * @event Player#progress 20557 * @type {EventTarget~Event} 20558 */ 20559/** 20560 * Retrigger the `progress` event that was triggered by the {@link Tech}. 20561 * 20562 * @private 20563 * @method Player#handleTechProgress_ 20564 * @fires Player#progress 20565 * @listens Tech#progress 20566 */ 20567'progress', 20568 20569/** 20570 * Fires when the loading of an audio/video is aborted. 20571 * 20572 * @event Player#abort 20573 * @type {EventTarget~Event} 20574 */ 20575/** 20576 * Retrigger the `abort` event that was triggered by the {@link Tech}. 20577 * 20578 * @private 20579 * @method Player#handleTechAbort_ 20580 * @fires Player#abort 20581 * @listens Tech#abort 20582 */ 20583'abort', 20584 20585/** 20586 * Fires when the browser is intentionally not getting media data. 20587 * 20588 * @event Player#suspend 20589 * @type {EventTarget~Event} 20590 */ 20591/** 20592 * Retrigger the `suspend` event that was triggered by the {@link Tech}. 20593 * 20594 * @private 20595 * @method Player#handleTechSuspend_ 20596 * @fires Player#suspend 20597 * @listens Tech#suspend 20598 */ 20599'suspend', 20600 20601/** 20602 * Fires when the current playlist is empty. 20603 * 20604 * @event Player#emptied 20605 * @type {EventTarget~Event} 20606 */ 20607/** 20608 * Retrigger the `emptied` event that was triggered by the {@link Tech}. 20609 * 20610 * @private 20611 * @method Player#handleTechEmptied_ 20612 * @fires Player#emptied 20613 * @listens Tech#emptied 20614 */ 20615'emptied', 20616/** 20617 * Fires when the browser is trying to get media data, but data is not available. 20618 * 20619 * @event Player#stalled 20620 * @type {EventTarget~Event} 20621 */ 20622/** 20623 * Retrigger the `stalled` event that was triggered by the {@link Tech}. 20624 * 20625 * @private 20626 * @method Player#handleTechStalled_ 20627 * @fires Player#stalled 20628 * @listens Tech#stalled 20629 */ 20630'stalled', 20631 20632/** 20633 * Fires when the browser has loaded meta data for the audio/video. 20634 * 20635 * @event Player#loadedmetadata 20636 * @type {EventTarget~Event} 20637 */ 20638/** 20639 * Retrigger the `stalled` event that was triggered by the {@link Tech}. 20640 * 20641 * @private 20642 * @method Player#handleTechLoadedmetadata_ 20643 * @fires Player#loadedmetadata 20644 * @listens Tech#loadedmetadata 20645 */ 20646'loadedmetadata', 20647 20648/** 20649 * Fires when the browser has loaded the current frame of the audio/video. 20650 * 20651 * @event Player#loadeddata 20652 * @type {event} 20653 */ 20654/** 20655 * Retrigger the `loadeddata` event that was triggered by the {@link Tech}. 20656 * 20657 * @private 20658 * @method Player#handleTechLoaddeddata_ 20659 * @fires Player#loadeddata 20660 * @listens Tech#loadeddata 20661 */ 20662'loadeddata', 20663 20664/** 20665 * Fires when the current playback position has changed. 20666 * 20667 * @event Player#timeupdate 20668 * @type {event} 20669 */ 20670/** 20671 * Retrigger the `timeupdate` event that was triggered by the {@link Tech}. 20672 * 20673 * @private 20674 * @method Player#handleTechTimeUpdate_ 20675 * @fires Player#timeupdate 20676 * @listens Tech#timeupdate 20677 */ 20678'timeupdate', 20679 20680/** 20681 * Fires when the playing speed of the audio/video is changed 20682 * 20683 * @event Player#ratechange 20684 * @type {event} 20685 */ 20686/** 20687 * Retrigger the `ratechange` event that was triggered by the {@link Tech}. 20688 * 20689 * @private 20690 * @method Player#handleTechRatechange_ 20691 * @fires Player#ratechange 20692 * @listens Tech#ratechange 20693 */ 20694'ratechange', 20695 20696/** 20697 * Fires when the video's intrinsic dimensions change 20698 * 20699 * @event Player#resize 20700 * @type {event} 20701 */ 20702/** 20703 * Retrigger the `resize` event that was triggered by the {@link Tech}. 20704 * 20705 * @private 20706 * @method Player#handleTechResize_ 20707 * @fires Player#resize 20708 * @listens Tech#resize 20709 */ 20710'resize', 20711 20712/** 20713 * Fires when the volume has been changed 20714 * 20715 * @event Player#volumechange 20716 * @type {event} 20717 */ 20718/** 20719 * Retrigger the `volumechange` event that was triggered by the {@link Tech}. 20720 * 20721 * @private 20722 * @method Player#handleTechVolumechange_ 20723 * @fires Player#volumechange 20724 * @listens Tech#volumechange 20725 */ 20726'volumechange', 20727 20728/** 20729 * Fires when the text track has been changed 20730 * 20731 * @event Player#texttrackchange 20732 * @type {event} 20733 */ 20734/** 20735 * Retrigger the `texttrackchange` event that was triggered by the {@link Tech}. 20736 * 20737 * @private 20738 * @method Player#handleTechTexttrackchange_ 20739 * @fires Player#texttrackchange 20740 * @listens Tech#texttrackchange 20741 */ 20742'texttrackchange']; 20743 20744/** 20745 * An instance of the `Player` class is created when any of the Video.js setup methods 20746 * are used to initialize a video. 20747 * 20748 * After an instance has been created it can be accessed globally in two ways: 20749 * 1. By calling `videojs('example_video_1');` 20750 * 2. By using it directly via `videojs.players.example_video_1;` 20751 * 20752 * @extends Component 20753 */ 20754 20755var Player = function (_Component) { 20756 inherits(Player, _Component); 20757 20758 /** 20759 * Create an instance of this class. 20760 * 20761 * @param {Element} tag
vendor: 24,713 bytes, lines 20762-21554
20762 * The original video DOM element used for configuring options. 20763 * 20764 * @param {Object} [options] 20765 * Object of option names and values. 20766 * 20767 * @param {Component~ReadyCallback} [ready] 20768 * Ready callback function. 20769 */ 20770 function Player(tag, options, ready) { 20771 classCallCheck(this, Player); 20772 20773 // Make sure tag ID exists 20774 tag.id = tag.id || 'vjs_video_' + newGUID(); 20775 20776 // Set Options 20777 // The options argument overrides options set in the video tag 20778 // which overrides globally set options. 20779 // This latter part coincides with the load order 20780 // (tag must exist before Player) 20781 options = assign(Player.getTagSettings(tag), options); 20782 20783 // Delay the initialization of children because we need to set up 20784 // player properties first, and can't use `this` before `super()` 20785 options.initChildren = false; 20786 20787 // Same with creating the element 20788 options.createEl = false; 20789 20790 // don't auto mixin the evented mixin 20791 options.evented = false; 20792 20793 // we don't want the player to report touch activity on itself 20794 // see enableTouchActivity in Component 20795 options.reportTouchActivity = false; 20796 20797 // If language is not set, get the closest lang attribute 20798 if (!options.language) { 20799 if (typeof tag.closest === 'function') { 20800 var closest = tag.closest('[lang]'); 20801 20802 if (closest && closest.getAttribute) { 20803 options.language = closest.getAttribute('lang'); 20804 } 20805 } else { 20806 var element = tag; 20807 20808 while (element && element.nodeType === 1) { 20809 if (getAttributes(element).hasOwnProperty('lang')) { 20810 options.language = element.getAttribute('lang'); 20811 break; 20812 } 20813 element = element.parentNode; 20814 } 20815 } 20816 } 20817 20818 // Run base component initializing with new options 20819 20820 // Turn off API access because we're loading a new tech that might load asynchronously 20821 var _this = possibleConstructorReturn(this, _Component.call(this, null, options, ready)); 20822 20823 _this.isReady_ = false; 20824 20825 // Init state hasStarted_ 20826 _this.hasStarted_ = false; 20827 20828 // Init state userActive_ 20829 _this.userActive_ = false; 20830 20831 // if the global option object was accidentally blown away by 20832 // someone, bail early with an informative error 20833 if (!_this.options_ || !_this.options_.techOrder || !_this.options_.techOrder.length) { 20834 throw new Error('No techOrder specified. Did you overwrite ' + 'videojs.options instead of just changing the ' + 'properties you want to override?'); 20835 } 20836 20837 // Store the original tag used to set options 20838 _this.tag = tag; 20839 20840 // Store the tag attributes used to restore html5 element 20841 _this.tagAttributes = tag && getAttributes(tag); 20842 20843 // Update current language 20844 _this.language(_this.options_.language); 20845 20846 // Update Supported Languages 20847 if (options.languages) { 20848 // Normalise player option languages to lowercase 20849 var languagesToLower = {}; 20850 20851 Object.getOwnPropertyNames(options.languages).forEach(function (name$$1) { 20852 languagesToLower[name$$1.toLowerCase()] = options.languages[name$$1]; 20853 }); 20854 _this.languages_ = languagesToLower; 20855 } else { 20856 _this.languages_ = Player.prototype.options_.languages; 20857 } 20858 20859 // Cache for video property values. 20860 _this.cache_ = {}; 20861 20862 // Set poster 20863 _this.poster_ = options.poster || ''; 20864 20865 // Set controls 20866 _this.controls_ = !!options.controls; 20867 20868 // Set default values for lastVolume 20869 _this.cache_.lastVolume = 1; 20870 20871 // Original tag settings stored in options 20872 // now remove immediately so native controls don't flash. 20873 // May be turned back on by HTML5 tech if nativeControlsForTouch is true 20874 tag.controls = false; 20875 tag.removeAttribute('controls'); 20876 20877 /* 20878 * Store the internal state of scrubbing 20879 * 20880 * @private 20881 * @return {Boolean} True if the user is scrubbing 20882 */ 20883 _this.scrubbing_ = false; 20884 20885 _this.el_ = _this.createEl(); 20886 20887 // Make this an evented object and use `el_` as its event bus. 20888 evented(_this, { eventBusKey: 'el_' }); 20889 20890 // We also want to pass the original player options to each component and plugin 20891 // as well so they don't need to reach back into the player for options later. 20892 // We also need to do another copy of this.options_ so we don't end up with 20893 // an infinite loop. 20894 var playerOptionsCopy = mergeOptions(_this.options_); 20895 20896 // Load plugins 20897 if (options.plugins) { 20898 var plugins = options.plugins; 20899 20900 Object.keys(plugins).forEach(function (name$$1) { 20901 if (typeof this[name$$1] === 'function') { 20902 this[name$$1](plugins[name$$1]); 20903 } else { 20904 throw new Error('plugin "' + name$$1 + '" does not exist'); 20905 } 20906 }, _this); 20907 } 20908 20909 _this.options_.playerOptions = playerOptionsCopy; 20910 20911 _this.middleware_ = []; 20912 20913 _this.initChildren(); 20914 20915 // Set isAudio based on whether or not an audio tag was used 20916 _this.isAudio(tag.nodeName.toLowerCase() === 'audio'); 20917 20918 // Update controls className. Can't do this when the controls are initially 20919 // set because the element doesn't exist yet. 20920 if (_this.controls()) { 20921 _this.addClass('vjs-controls-enabled'); 20922 } else { 20923 _this.addClass('vjs-controls-disabled'); 20924 } 20925 20926 // Set ARIA label and region role depending on player type 20927 _this.el_.setAttribute('role', 'region'); 20928 if (_this.isAudio()) { 20929 _this.el_.setAttribute('aria-label', _this.localize('Audio Player')); 20930 } else { 20931 _this.el_.setAttribute('aria-label', _this.localize('Video Player')); 20932 } 20933 20934 if (_this.isAudio()) { 20935 _this.addClass('vjs-audio'); 20936 } 20937 20938 if (_this.flexNotSupported_()) { 20939 _this.addClass('vjs-no-flex'); 20940 } 20941 20942 // TODO: Make this smarter. Toggle user state between touching/mousing 20943 // using events, since devices can have both touch and mouse events. 20944 // if (browser.TOUCH_ENABLED) { 20945 // this.addClass('vjs-touch-enabled'); 20946 // } 20947 20948 // iOS Safari has broken hover handling 20949 if (!IS_IOS) { 20950 _this.addClass('vjs-workinghover'); 20951 } 20952 20953 // Make player easily findable by ID 20954 Player.players[_this.id_] = _this; 20955 20956 // Add a major version class to aid css in plugins 20957 var majorVersion = version.split('.')[0]; 20958 20959 _this.addClass('vjs-v' + majorVersion); 20960 20961 // When the player is first initialized, trigger activity so components 20962 // like the control bar show themselves if needed 20963 _this.userActive(true); 20964 _this.reportUserActivity(); 20965 _this.listenForUserActivity_(); 20966 20967 _this.on('fullscreenchange', _this.handleFullscreenChange_); 20968 _this.on('stageclick', _this.handleStageClick_); 20969 20970 _this.changingSrc_ = false; 20971 _this.playWaitingForReady_ = false; 20972 _this.playOnLoadstart_ = null; 20973 20974 _this.forceAutoplayInChrome_(); 20975 return _this; 20976 } 20977 20978 /** 20979 * Destroys the video player and does any necessary cleanup. 20980 * 20981 * This is especially helpful if you are dynamically adding and removing videos 20982 * to/from the DOM. 20983 * 20984 * @fires Player#dispose 20985 */ 20986 20987 20988 Player.prototype.dispose = function dispose() { 20989 /** 20990 * Called when the player is being disposed of. 20991 * 20992 * @event Player#dispose 20993 * @type {EventTarget~Event} 20994 */ 20995 this.trigger('dispose'); 20996 // prevent dispose from being called twice 20997 this.off('dispose'); 20998 20999 if (this.styleEl_ && this.styleEl_.parentNode) { 21000 this.styleEl_.parentNode.removeChild(this.styleEl_); 21001 this.styleEl_ = null; 21002 } 21003 21004 // Kill reference to this player 21005 Player.players[this.id_] = null; 21006 21007 if (this.tag && this.tag.player) { 21008 this.tag.player = null; 21009 } 21010 21011 if (this.el_ && this.el_.player) { 21012 this.el_.player = null; 21013 } 21014 21015 if (this.tech_) { 21016 this.tech_.dispose(); 21017 } 21018 21019 if (this.playerElIngest_) { 21020 this.playerElIngest_ = null; 21021 } 21022 21023 if (this.tag) { 21024 this.tag = null; 21025 } 21026 21027 // the actual .el_ is removed here 21028 _Component.prototype.dispose.call(this); 21029 }; 21030 21031 /** 21032 * Create the `Player`'s DOM element. 21033 * 21034 * @return {Element} 21035 * The DOM element that gets created. 21036 */ 21037 21038 21039 Player.prototype.createEl = function createEl$$1() { 21040 var tag = this.tag; 21041 var el = void 0; 21042 var playerElIngest = this.playerElIngest_ = tag.parentNode && tag.parentNode.hasAttribute && tag.parentNode.hasAttribute('data-vjs-player'); 21043 var divEmbed = this.tag.tagName.toLowerCase() === 'video-js'; 21044 21045 if (playerElIngest) { 21046 el = this.el_ = tag.parentNode; 21047 } else if (!divEmbed) { 21048 el = this.el_ = _Component.prototype.createEl.call(this, 'div'); 21049 } 21050 21051 // Copy over all the attributes from the tag, including ID and class 21052 // ID will now reference player box, not the video tag 21053 var attrs = getAttributes(tag); 21054 21055 if (divEmbed) { 21056 el = this.el_ = tag; 21057 tag = this.tag = document_1.createElement('video'); 21058 while (el.children.length) { 21059 tag.appendChild(el.firstChild); 21060 } 21061 21062 if (!hasClass(el, 'video-js')) { 21063 addClass(el, 'video-js'); 21064 } 21065 21066 el.appendChild(tag); 21067 21068 playerElIngest = this.playerElIngest_ = el; 21069 } 21070 21071 // set tabindex to -1 so we could focus on the player element 21072 tag.setAttribute('tabindex', '-1'); 21073 21074 // Remove width/height attrs from tag so CSS can make it 100% width/height 21075 tag.removeAttribute('width'); 21076 tag.removeAttribute('height'); 21077 21078 Object.getOwnPropertyNames(attrs).forEach(function (attr) { 21079 // workaround so we don't totally break IE7 21080 // http://stackoverflow.com/questions/3653444/css-styles-not-applied-on-dynamic-elements-in-internet-explorer-7 21081 if (attr === 'class') { 21082 el.className += ' ' + attrs[attr]; 21083 21084 if (divEmbed) { 21085 tag.className += ' ' + attrs[attr]; 21086 } 21087 } else { 21088 el.setAttribute(attr, attrs[attr]); 21089 21090 if (divEmbed) { 21091 tag.setAttribute(attr, attrs[attr]); 21092 } 21093 } 21094 }); 21095 21096 // Update tag id/class for use as HTML5 playback tech 21097 // Might think we should do this after embedding in container so .vjs-tech class 21098 // doesn't flash 100% width/height, but class only applies with .video-js parent 21099 tag.playerId = tag.id; 21100 tag.id += '_html5_api'; 21101 tag.className = 'vjs-tech'; 21102 21103 // Make player findable on elements 21104 tag.player = el.player = this; 21105 // Default state of video is paused 21106 this.addClass('vjs-paused'); 21107 21108 // Add a style element in the player that we'll use to set the width/height 21109 // of the player in a way that's still overrideable by CSS, just like the 21110 // video element 21111 if (window_1.VIDEOJS_NO_DYNAMIC_STYLE !== true) { 21112 this.styleEl_ = createStyleElement('vjs-styles-dimensions'); 21113 var defaultsStyleEl = $('.vjs-styles-defaults'); 21114 var head = $('head'); 21115 21116 head.insertBefore(this.styleEl_, defaultsStyleEl ? defaultsStyleEl.nextSibling : head.firstChild); 21117 } 21118 21119 // Pass in the width/height/aspectRatio options which will update the style el 21120 this.width(this.options_.width); 21121 this.height(this.options_.height); 21122 this.fluid(this.options_.fluid); 21123 this.aspectRatio(this.options_.aspectRatio); 21124 21125 // Hide any links within the video/audio tag, because IE doesn't hide them completely. 21126 var links = tag.getElementsByTagName('a'); 21127 21128 for (var i = 0; i < links.length; i++) { 21129 var linkEl = links.item(i); 21130 21131 addClass(linkEl, 'vjs-hidden'); 21132 linkEl.setAttribute('hidden', 'hidden'); 21133 } 21134 21135 // insertElFirst seems to cause the networkState to flicker from 3 to 2, so 21136 // keep track of the original for later so we can know if the source originally failed 21137 tag.initNetworkState_ = tag.networkState; 21138 21139 // Wrap video tag in div (el/box) container 21140 if (tag.parentNode && !playerElIngest) { 21141 tag.parentNode.insertBefore(el, tag); 21142 } 21143 21144 // insert the tag as the first child of the player element 21145 // then manually add it to the children array so that this.addChild 21146 // will work properly for other components 21147 // 21148 // Breaks iPhone, fixed in HTML5 setup. 21149 prependTo(tag, el); 21150 this.children_.unshift(tag); 21151 21152 // Set lang attr on player to ensure CSS :lang() in consistent with player 21153 // if it's been set to something different to the doc 21154 this.el_.setAttribute('lang', this.language_); 21155 21156 this.el_ = el; 21157 21158 return el; 21159 }; 21160 21161 /** 21162 * A getter/setter for the `Player`'s width. Returns the player's configured value. 21163 * To get the current width use `currentWidth()`. 21164 * 21165 * @param {number} [value] 21166 * The value to set the `Player`'s width to. 21167 * 21168 * @param {boolean} [skipListeners] 21169 * Skip the playerresize event trigger 21170 * 21171 * @return {number} 21172 * The current width of the `Player` when getting. 21173 */ 21174 21175 21176 Player.prototype.width = function width(value, skipListeners) { 21177 return this.dimension('width', value, skipListeners); 21178 }; 21179 21180 /** 21181 * A getter/setter for the `Player`'s height. Returns the player's configured value. 21182 * To get the current height use `currentheight()`. 21183 * 21184 * @param {number} [value] 21185 * The value to set the `Player`'s heigth to. 21186 * 21187 * @param {boolean} [skipListeners] 21188 * Skip the playerresize event trigger 21189 * 21190 * @return {number} 21191 * The current height of the `Player` when getting. 21192 */ 21193 21194 21195 Player.prototype.height = function height(value, skipListeners) { 21196 return this.dimension('height', value, skipListeners); 21197 }; 21198 21199 /** 21200 * A getter/setter for the `Player`'s width & height. 21201 * 21202 * @fires Player#playerresize 21203 * 21204 * @param {string} dimension 21205 * This string can be: 21206 * - 'width' 21207 * - 'height' 21208 * 21209 * @param {number} [value] 21210 * Value for dimension specified in the first argument. 21211 * 21212 * @param {boolean} [skipListeners] 21213 * Skip the playerresize event trigger 21214 * 21215 * @return {number} 21216 * The dimension arguments value when getting (width/height). 21217 */ 21218 21219 21220 Player.prototype.dimension = function dimension(_dimension, value, skipListeners) { 21221 var privDimension = _dimension + '_'; 21222 21223 if (value === undefined) { 21224 return this[privDimension] || 0; 21225 } 21226 21227 if (value === '') { 21228 // If an empty string is given, reset the dimension to be automatic 21229 this[privDimension] = undefined; 21230 this.updateStyleEl_(); 21231 return; 21232 } 21233 21234 var parsedVal = parseFloat(value); 21235 21236 if (isNaN(parsedVal)) { 21237 log$1.error('Improper value "' + value + '" supplied for for ' + _dimension); 21238 return; 21239 } 21240 21241 this[privDimension] = parsedVal; 21242 this.updateStyleEl_(); 21243 21244 // skipListeners allows us to avoid triggering the resize event when setting both width and height 21245 if (this.isReady_ && !skipListeners) { 21246 /** 21247 * Triggered when the player is resized. 21248 * 21249 * @event Player#playerresize 21250 * @type {EventTarget~Event} 21251 */ 21252 this.trigger('playerresize'); 21253 } 21254 }; 21255 21256 /** 21257 * A getter/setter/toggler for the vjs-fluid `className` on the `Player`. 21258 * 21259 * @param {boolean} [bool] 21260 * - A value of true adds the class. 21261 * - A value of false removes the class. 21262 * - No value will toggle the fluid class. 21263 * 21264 * @return {boolean|undefined} 21265 * - The value of fluid when getting. 21266 * - `undefined` when setting. 21267 */ 21268 21269 21270 Player.prototype.fluid = function fluid(bool) { 21271 if (bool === undefined) { 21272 return !!this.fluid_; 21273 } 21274 21275 this.fluid_ = !!bool; 21276 21277 if (bool) { 21278 this.addClass('vjs-fluid'); 21279 } else { 21280 this.removeClass('vjs-fluid'); 21281 } 21282 21283 this.updateStyleEl_(); 21284 }; 21285 21286 /** 21287 * Get/Set the aspect ratio 21288 * 21289 * @param {string} [ratio] 21290 * Aspect ratio for player 21291 * 21292 * @return {string|undefined} 21293 * returns the current aspect ratio when getting 21294 */ 21295 21296 /** 21297 * A getter/setter for the `Player`'s aspect ratio. 21298 * 21299 * @param {string} [ratio] 21300 * The value to set the `Player's aspect ratio to. 21301 * 21302 * @return {string|undefined} 21303 * - The current aspect ratio of the `Player` when getting. 21304 * - undefined when setting 21305 */ 21306 21307 21308 Player.prototype.aspectRatio = function aspectRatio(ratio) { 21309 if (ratio === undefined) { 21310 return this.aspectRatio_; 21311 } 21312 21313 // Check for width:height format 21314 if (!/^\d+\:\d+$/.test(ratio)) { 21315 throw new Error('Improper value supplied for aspect ratio. The format should be width:height, for example 16:9.'); 21316 } 21317 this.aspectRatio_ = ratio; 21318 21319 // We're assuming if you set an aspect ratio you want fluid mode, 21320 // because in fixed mode you could calculate width and height yourself. 21321 this.fluid(true); 21322 21323 this.updateStyleEl_(); 21324 }; 21325 21326 /** 21327 * Update styles of the `Player` element (height, width and aspect ratio). 21328 * 21329 * @private 21330 * @listens Tech#loadedmetadata 21331 */ 21332 21333 21334 Player.prototype.updateStyleEl_ = function updateStyleEl_() { 21335 if (window_1.VIDEOJS_NO_DYNAMIC_STYLE === true) { 21336 var _width = typeof this.width_ === 'number' ? this.width_ : this.options_.width; 21337 var _height = typeof this.height_ === 'number' ? this.height_ : this.options_.height; 21338 var techEl = this.tech_ && this.tech_.el(); 21339 21340 if (techEl) { 21341 if (_width >= 0) { 21342 techEl.width = _width; 21343 } 21344 if (_height >= 0) { 21345 techEl.height = _height; 21346 } 21347 } 21348 21349 return; 21350 } 21351 21352 var width = void 0; 21353 var height = void 0; 21354 var aspectRatio = void 0; 21355 var idClass = void 0; 21356 21357 // The aspect ratio is either used directly or to calculate width and height. 21358 if (this.aspectRatio_ !== undefined && this.aspectRatio_ !== 'auto') { 21359 // Use any aspectRatio that's been specifically set 21360 aspectRatio = this.aspectRatio_; 21361 } else if (this.videoWidth() > 0) { 21362 // Otherwise try to get the aspect ratio from the video metadata 21363 aspectRatio = this.videoWidth() + ':' + this.videoHeight(); 21364 } else { 21365 // Or use a default. The video element's is 2:1, but 16:9 is more common. 21366 aspectRatio = '16:9'; 21367 } 21368 21369 // Get the ratio as a decimal we can use to calculate dimensions 21370 var ratioParts = aspectRatio.split(':'); 21371 var ratioMultiplier = ratioParts[1] / ratioParts[0]; 21372 21373 if (this.width_ !== undefined) { 21374 // Use any width that's been specifically set 21375 width = this.width_; 21376 } else if (this.height_ !== undefined) { 21377 // Or calulate the width from the aspect ratio if a height has been set 21378 width = this.height_ / ratioMultiplier; 21379 } else { 21380 // Or use the video's metadata, or use the video el's default of 300 21381 width = this.videoWidth() || 300; 21382 } 21383 21384 if (this.height_ !== undefined) { 21385 // Use any height that's been specifically set 21386 height = this.height_; 21387 } else { 21388 // Otherwise calculate the height from the ratio and the width 21389 height = width * ratioMultiplier; 21390 } 21391 21392 // Ensure the CSS class is valid by starting with an alpha character 21393 if (/^[^a-zA-Z]/.test(this.id())) { 21394 idClass = 'dimensions-' + this.id(); 21395 } else { 21396 idClass = this.id() + '-dimensions'; 21397 } 21398 21399 // Ensure the right class is still on the player for the style element 21400 this.addClass(idClass); 21401 21402 setTextContent(this.styleEl_, '\n .' + idClass + ' {\n width: ' + width + 'px;\n height: ' + height + 'px;\n }\n\n .' + idClass + '.vjs-fluid {\n padding-top: ' + ratioMultiplier * 100 + '%;\n }\n '); 21403 }; 21404 21405 /** 21406 * Load/Create an instance of playback {@link Tech} including element 21407 * and API methods. Then append the `Tech` element in `Player` as a child. 21408 * 21409 * @param {string} techName 21410 * name of the playback technology 21411 * 21412 * @param {string} source 21413 * video source 21414 * 21415 * @private 21416 */ 21417 21418 21419 Player.prototype.loadTech_ = function loadTech_(techName, source) { 21420 var _this2 = this; 21421 21422 // Pause and remove current playback technology 21423 if (this.tech_) { 21424 this.unloadTech_(); 21425 } 21426 21427 var titleTechName = toTitleCase(techName); 21428 var camelTechName = techName.charAt(0).toLowerCase() + techName.slice(1); 21429 21430 // get rid of the HTML5 video tag as soon as we are using another tech 21431 if (titleTechName !== 'Html5' && this.tag) { 21432 Tech.getTech('Html5').disposeMediaElement(this.tag); 21433 this.tag.player = null; 21434 this.tag = null; 21435 } 21436 21437 this.techName_ = titleTechName; 21438 21439 // Turn off API access because we're loading a new tech that might load asynchronously 21440 this.isReady_ = false; 21441 21442 // Grab tech-specific options from player options and add source and parent element to use. 21443 var techOptions = { 21444 source: source, 21445 'nativeControlsForTouch': this.options_.nativeControlsForTouch, 21446 'playerId': this.id(), 21447 'techId': this.id() + '_' + titleTechName + '_api', 21448 'autoplay': this.options_.autoplay, 21449 'playsinline': this.options_.playsinline, 21450 'preload': this.options_.preload, 21451 'loop': this.options_.loop, 21452 'muted': this.options_.muted, 21453 'poster': this.poster(), 21454 'language': this.language(), 21455 'playerElIngest': this.playerElIngest_ || false, 21456 'vtt.js': this.options_['vtt.js'] 21457 }; 21458 21459 ALL.names.forEach(function (name$$1) { 21460 var props = ALL[name$$1]; 21461 21462 techOptions[props.getterName] = _this2[props.privateName]; 21463 }); 21464 21465 assign(techOptions, this.options_[titleTechName]); 21466 assign(techOptions, this.options_[camelTechName]); 21467 assign(techOptions, this.options_[techName.toLowerCase()]); 21468 21469 if (this.tag) { 21470 techOptions.tag = this.tag; 21471 } 21472 21473 if (source && source.src === this.cache_.src && this.cache_.currentTime > 0) { 21474 techOptions.startTime = this.cache_.currentTime; 21475 } 21476 21477 // Initialize tech instance 21478 var TechClass = Tech.getTech(techName); 21479 21480 if (!TechClass) { 21481 throw new Error('No Tech named \'' + titleTechName + '\' exists! \'' + titleTechName + '\' should be registered using videojs.registerTech()\''); 21482 } 21483 21484 this.tech_ = new TechClass(techOptions); 21485 21486 // player.triggerReady is always async, so don't need this to be async 21487 this.tech_.ready(bind(this, this.handleTechReady_), true); 21488 21489 textTrackConverter.jsonToTextTracks(this.textTracksJson_ || [], this.tech_); 21490 21491 // Listen to all HTML5-defined events and trigger them on the player 21492 TECH_EVENTS_RETRIGGER.forEach(function (event) { 21493 _this2.on(_this2.tech_, event, _this2['handleTech' + toTitleCase(event) + '_']); 21494 }); 21495 this.on(this.tech_, 'loadstart', this.handleTechLoadStart_); 21496 this.on(this.tech_, 'waiting', this.handleTechWaiting_); 21497 this.on(this.tech_, 'canplay', this.handleTechCanPlay_); 21498 this.on(this.tech_, 'canplaythrough', this.handleTechCanPlayThrough_); 21499 this.on(this.tech_, 'playing', this.handleTechPlaying_); 21500 this.on(this.tech_, 'ended', this.handleTechEnded_); 21501 this.on(this.tech_, 'seeking', this.handleTechSeeking_); 21502 this.on(this.tech_, 'seeked', this.handleTechSeeked_); 21503 this.on(this.tech_, 'play', this.handleTechPlay_); 21504 this.on(this.tech_, 'firstplay', this.handleTechFirstPlay_); 21505 this.on(this.tech_, 'pause', this.handleTechPause_); 21506 this.on(this.tech_, 'durationchange', this.handleTechDurationChange_); 21507 this.on(this.tech_, 'fullscreenchange', this.handleTechFullscreenChange_); 21508 this.on(this.tech_, 'error', this.handleTechError_); 21509 this.on(this.tech_, 'loadedmetadata', this.updateStyleEl_); 21510 this.on(this.tech_, 'posterchange', this.handleTechPosterChange_); 21511 this.on(this.tech_, 'textdata', this.handleTechTextData_); 21512 21513 this.usingNativeControls(this.techGet_('controls')); 21514 21515 if (this.controls() && !this.usingNativeControls()) { 21516 this.addTechControlsListeners_(); 21517 } 21518 21519 // Add the tech element in the DOM if it was not already there 21520 // Make sure to not insert the original video element if using Html5 21521 if (this.tech_.el().parentNode !== this.el() && (titleTechName !== 'Html5' || !this.tag)) { 21522 prependTo(this.tech_.el(), this.el()); 21523 } 21524 21525 // Get rid of the original video tag reference after the first tech is loaded 21526 if (this.tag) { 21527 this.tag.player = null; 21528 this.tag = null; 21529 } 21530 }; 21531 21532 /** 21533 * Unload and dispose of the current playback {@link Tech}. 21534 * 21535 * @private 21536 */ 21537 21538 21539 Player.prototype.unloadTech_ = function unloadTech_() { 21540 var _this3 = this; 21541 21542 // Save the current text tracks so that we can reuse the same text tracks with the next tech 21543 ALL.names.forEach(function (name$$1) { 21544 var props = ALL[name$$1]; 21545 21546 _this3[props.privateName] = _this3[props.getterName](); 21547 }); 21548 this.textTracksJson_ = textTrackConverter.textTracksToJson(this.tech_); 21549 21550 this.isReady_ = false; 21551 21552 this.tech_.dispose(); 21553 21554 this.tech_ = false;
21555 }; 21556 21557 /** 21558 * Return a reference to the current {@link Tech}. 21559 * It will print a warning by default about the danger of using the tech directly 21560 * but any argument that is passed in will silence the warning. 21561 * 21562 * @param {*} [safety] 21563 * Anything passed in to silence the warning 21564 * 21565 * @return {Tech} 21566 * The Tech 21567 */ 21568 21569 21570 Player.prototype.tech = function tech(safety) { 21571 if (safety === undefined) { 21572 log$1.warn(tsml(_templateObject$1)); 21573 } 21574 21575 return this.tech_; 21576 }; 21577 21578 /** 21579 * Set up click and touch listeners for the playback element 21580 * 21581 * - On desktops: a click on the video itself will toggle playback 21582 * - On mobile devices: a click on the video toggles controls 21583 * which is done by toggling the user state between active and 21584 * inactive 21585 * - A tap can signal that a user has become active or has become inactive 21586 * e.g. a quick tap on an iPhone movie should reveal the controls. Another 21587 * quick tap should hide them again (signaling the user is in an inactive 21588 * viewing state) 21589 * - In addition to this, we still want the user to be considered inactive after 21590 * a few seconds of inactivity. 21591 * 21592 * > Note: the only part of iOS interaction we can't mimic with this setup 21593 * is a touch and hold on the video element counting as activity in order to 21594 * keep the controls showing, but that shouldn't be an issue. A touch and hold 21595 * on any controls will still keep the user active 21596 * 21597 * @private 21598 */ 21599 21600 21601 Player.prototype.addTechControlsListeners_ = function addTechControlsListeners_() { 21602 // Make sure to remove all the previous listeners in case we are called multiple times. 21603 this.removeTechControlsListeners_(); 21604 21605 // Some browsers (Chrome & IE) don't trigger a click on a flash swf, but do 21606 // trigger mousedown/up. 21607 // http://stackoverflow.com/questions/1444562/javascript-onclick-event-over-flash-object 21608 // Any touch events are set to block the mousedown event from happening 21609 this.on(this.tech_, 'mousedown', this.handleTechClick_); 21610 21611 // If the controls were hidden we don't want that to change without a tap event 21612 // so we'll check if the controls were already showing before reporting user 21613 // activity 21614 this.on(this.tech_, 'touchstart', this.handleTechTouchStart_); 21615 this.on(this.tech_, 'touchmove', this.handleTechTouchMove_); 21616 this.on(this.tech_, 'touchend', this.handleTechTouchEnd_); 21617 21618 // The tap listener needs to come after the touchend listener because the tap 21619 // listener cancels out any reportedUserActivity when setting userActive(false) 21620 this.on(this.tech_, 'tap', this.handleTechTap_); 21621 }; 21622 21623 /** 21624 * Remove the listeners used for click and tap controls. This is needed for 21625 * toggling to controls disabled, where a tap/touch should do nothing. 21626 * 21627 * @private 21628 */ 21629 21630 21631 Player.prototype.removeTechControlsListeners_ = function removeTechControlsListeners_() { 21632 // We don't want to just use `this.off()` because there might be other needed 21633 // listeners added by techs that extend this. 21634 this.off(this.tech_, 'tap', this.handleTechTap_); 21635 this.off(this.tech_, 'touchstart', this.handleTechTouchStart_); 21636 this.off(this.tech_, 'touchmove', this.handleTechTouchMove_); 21637 this.off(this.tech_, 'touchend', this.handleTechTouchEnd_); 21638 this.off(this.tech_, 'mousedown', this.handleTechClick_); 21639 }; 21640 21641 /** 21642 * Player waits for the tech to be ready 21643 * 21644 * @private 21645 */ 21646 21647 21648 Player.prototype.handleTechReady_ = function handleTechReady_() { 21649 this.triggerReady(); 21650 21651 // Keep the same volume as before 21652 if (this.cache_.volume) { 21653 this.techCall_('setVolume', this.cache_.volume); 21654 } 21655 21656 // Look if the tech found a higher resolution poster while loading 21657 this.handleTechPosterChange_(); 21658 21659 // Update the duration if available 21660 this.handleTechDurationChange_(); 21661 21662 // Chrome and Safari both have issues with autoplay. 21663 // In Safari (5.1.1), when we move the video element into the container div, autoplay doesn't work. 21664 // In Chrome (15), if you have autoplay + a poster + no controls, the video gets hidden (but audio plays) 21665 // This fixes both issues. Need to wait for API, so it updates displays correctly 21666 if ((this.src() || this.currentSrc()) && this.tag && this.options_.autoplay && this.paused()) {
vendor: 24,027 bytes, lines 21667-22615
21667 try { 21668 // Chrome Fix. Fixed in Chrome v16. 21669 delete this.tag.poster; 21670 } catch (e) { 21671 log$1('deleting tag.poster throws in some browsers', e); 21672 } 21673 } 21674 }; 21675 21676 /** 21677 * Retrigger the `loadstart` event that was triggered by the {@link Tech}. This 21678 * function will also trigger {@link Player#firstplay} if it is the first loadstart 21679 * for a video. 21680 * 21681 * @fires Player#loadstart 21682 * @fires Player#firstplay 21683 * @listens Tech#loadstart 21684 * @private 21685 */ 21686 21687 21688 Player.prototype.handleTechLoadStart_ = function handleTechLoadStart_() { 21689 // TODO: Update to use `emptied` event instead. See #1277. 21690 21691 this.removeClass('vjs-ended'); 21692 this.removeClass('vjs-seeking'); 21693 21694 // reset the error state 21695 this.error(null); 21696 21697 // If it's already playing we want to trigger a firstplay event now. 21698 // The firstplay event relies on both the play and loadstart events 21699 // which can happen in any order for a new source 21700 if (!this.paused()) { 21701 /** 21702 * Fired when the user agent begins looking for media data 21703 * 21704 * @event Player#loadstart 21705 * @type {EventTarget~Event} 21706 */ 21707 this.trigger('loadstart'); 21708 this.trigger('firstplay'); 21709 } else { 21710 // reset the hasStarted state 21711 this.hasStarted(false); 21712 this.trigger('loadstart'); 21713 } 21714 }; 21715 21716 /** 21717 * Add/remove the vjs-has-started class 21718 * 21719 * @fires Player#firstplay 21720 * 21721 * @param {boolean} request 21722 * - true: adds the class 21723 * - false: remove the class 21724 * 21725 * @return {boolean} 21726 * the boolean value of hasStarted_ 21727 */ 21728 21729 21730 Player.prototype.hasStarted = function hasStarted(request) { 21731 if (request === undefined) { 21732 // act as getter, if we have no request to change 21733 return this.hasStarted_; 21734 } 21735 21736 if (request === this.hasStarted_) { 21737 return; 21738 } 21739 21740 this.hasStarted_ = request; 21741 21742 if (this.hasStarted_) { 21743 this.addClass('vjs-has-started'); 21744 this.trigger('firstplay'); 21745 } else { 21746 this.removeClass('vjs-has-started'); 21747 } 21748 }; 21749 21750 /** 21751 * Fired whenever the media begins or resumes playback 21752 * 21753 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-play} 21754 * @fires Player#play 21755 * @listens Tech#play 21756 * @private 21757 */ 21758 21759 21760 Player.prototype.handleTechPlay_ = function handleTechPlay_() { 21761 this.removeClass('vjs-ended'); 21762 this.removeClass('vjs-paused'); 21763 this.addClass('vjs-playing'); 21764 21765 // hide the poster when the user hits play 21766 this.hasStarted(true); 21767 /** 21768 * Triggered whenever an {@link Tech#play} event happens. Indicates that 21769 * playback has started or resumed. 21770 * 21771 * @event Player#play 21772 * @type {EventTarget~Event} 21773 */ 21774 this.trigger('play'); 21775 }; 21776 21777 /** 21778 * Retrigger the `waiting` event that was triggered by the {@link Tech}. 21779 * 21780 * @fires Player#waiting 21781 * @listens Tech#waiting 21782 * @private 21783 */ 21784 21785 21786 Player.prototype.handleTechWaiting_ = function handleTechWaiting_() { 21787 var _this4 = this; 21788 21789 this.addClass('vjs-waiting'); 21790 /** 21791 * A readyState change on the DOM element has caused playback to stop. 21792 * 21793 * @event Player#waiting 21794 * @type {EventTarget~Event} 21795 */ 21796 this.trigger('waiting'); 21797 this.one('timeupdate', function () { 21798 return _this4.removeClass('vjs-waiting'); 21799 }); 21800 }; 21801 21802 /** 21803 * Retrigger the `canplay` event that was triggered by the {@link Tech}. 21804 * > Note: This is not consistent between browsers. See #1351 21805 * 21806 * @fires Player#canplay 21807 * @listens Tech#canplay 21808 * @private 21809 */ 21810 21811 21812 Player.prototype.handleTechCanPlay_ = function handleTechCanPlay_() { 21813 this.removeClass('vjs-waiting'); 21814 /** 21815 * The media has a readyState of HAVE_FUTURE_DATA or greater. 21816 * 21817 * @event Player#canplay 21818 * @type {EventTarget~Event} 21819 */ 21820 this.trigger('canplay'); 21821 }; 21822 21823 /** 21824 * Retrigger the `canplaythrough` event that was triggered by the {@link Tech}. 21825 * 21826 * @fires Player#canplaythrough 21827 * @listens Tech#canplaythrough 21828 * @private 21829 */ 21830 21831 21832 Player.prototype.handleTechCanPlayThrough_ = function handleTechCanPlayThrough_() { 21833 this.removeClass('vjs-waiting'); 21834 /** 21835 * The media has a readyState of HAVE_ENOUGH_DATA or greater. This means that the 21836 * entire media file can be played without buffering. 21837 * 21838 * @event Player#canplaythrough 21839 * @type {EventTarget~Event} 21840 */ 21841 this.trigger('canplaythrough'); 21842 }; 21843 21844 /** 21845 * Retrigger the `playing` event that was triggered by the {@link Tech}. 21846 * 21847 * @fires Player#playing 21848 * @listens Tech#playing 21849 * @private 21850 */ 21851 21852 21853 Player.prototype.handleTechPlaying_ = function handleTechPlaying_() { 21854 this.removeClass('vjs-waiting'); 21855 /** 21856 * The media is no longer blocked from playback, and has started playing. 21857 * 21858 * @event Player#playing 21859 * @type {EventTarget~Event} 21860 */ 21861 this.trigger('playing'); 21862 }; 21863 21864 /** 21865 * Retrigger the `seeking` event that was triggered by the {@link Tech}. 21866 * 21867 * @fires Player#seeking 21868 * @listens Tech#seeking 21869 * @private 21870 */ 21871 21872 21873 Player.prototype.handleTechSeeking_ = function handleTechSeeking_() { 21874 this.addClass('vjs-seeking'); 21875 /** 21876 * Fired whenever the player is jumping to a new time 21877 * 21878 * @event Player#seeking 21879 * @type {EventTarget~Event} 21880 */ 21881 this.trigger('seeking'); 21882 }; 21883 21884 /** 21885 * Retrigger the `seeked` event that was triggered by the {@link Tech}. 21886 * 21887 * @fires Player#seeked 21888 * @listens Tech#seeked 21889 * @private 21890 */ 21891 21892 21893 Player.prototype.handleTechSeeked_ = function handleTechSeeked_() { 21894 this.removeClass('vjs-seeking'); 21895 /** 21896 * Fired when the player has finished jumping to a new time 21897 * 21898 * @event Player#seeked 21899 * @type {EventTarget~Event} 21900 */ 21901 this.trigger('seeked'); 21902 }; 21903 21904 /** 21905 * Retrigger the `firstplay` event that was triggered by the {@link Tech}. 21906 * 21907 * @fires Player#firstplay 21908 * @listens Tech#firstplay 21909 * @deprecated As of 6.0 firstplay event is deprecated. 21910 * @deprecated As of 6.0 passing the `starttime` option to the player and the firstplay event are deprecated. 21911 * @private 21912 */ 21913 21914 21915 Player.prototype.handleTechFirstPlay_ = function handleTechFirstPlay_() { 21916 // If the first starttime attribute is specified 21917 // then we will start at the given offset in seconds 21918 if (this.options_.starttime) { 21919 log$1.warn('Passing the `starttime` option to the player will be deprecated in 6.0'); 21920 this.currentTime(this.options_.starttime); 21921 } 21922 21923 this.addClass('vjs-has-started'); 21924 /** 21925 * Fired the first time a video is played. Not part of the HLS spec, and this is 21926 * probably not the best implementation yet, so use sparingly. If you don't have a 21927 * reason to prevent playback, use `myPlayer.one('play');` instead. 21928 * 21929 * @event Player#firstplay 21930 * @deprecated As of 6.0 firstplay event is deprecated. 21931 * @type {EventTarget~Event} 21932 */ 21933 this.trigger('firstplay'); 21934 }; 21935 21936 /** 21937 * Retrigger the `pause` event that was triggered by the {@link Tech}. 21938 * 21939 * @fires Player#pause 21940 * @listens Tech#pause 21941 * @private 21942 */ 21943 21944 21945 Player.prototype.handleTechPause_ = function handleTechPause_() { 21946 this.removeClass('vjs-playing'); 21947 this.addClass('vjs-paused'); 21948 /** 21949 * Fired whenever the media has been paused 21950 * 21951 * @event Player#pause 21952 * @type {EventTarget~Event} 21953 */ 21954 this.trigger('pause'); 21955 }; 21956 21957 /** 21958 * Retrigger the `ended` event that was triggered by the {@link Tech}. 21959 * 21960 * @fires Player#ended 21961 * @listens Tech#ended 21962 * @private 21963 */ 21964 21965 21966 Player.prototype.handleTechEnded_ = function handleTechEnded_() { 21967 this.addClass('vjs-ended'); 21968 if (this.options_.loop) { 21969 this.currentTime(0); 21970 this.play(); 21971 } else if (!this.paused()) { 21972 this.pause(); 21973 } 21974 21975 /** 21976 * Fired when the end of the media resource is reached (currentTime == duration) 21977 * 21978 * @event Player#ended 21979 * @type {EventTarget~Event} 21980 */ 21981 this.trigger('ended'); 21982 }; 21983 21984 /** 21985 * Fired when the duration of the media resource is first known or changed 21986 * 21987 * @listens Tech#durationchange 21988 * @private 21989 */ 21990 21991 21992 Player.prototype.handleTechDurationChange_ = function handleTechDurationChange_() { 21993 this.duration(this.techGet_('duration')); 21994 }; 21995 21996 /** 21997 * Handle a click on the media element to play/pause 21998 * 21999 * @param {EventTarget~Event} event 22000 * the event that caused this function to trigger 22001 * 22002 * @listens Tech#mousedown 22003 * @private 22004 */ 22005 22006 22007 Player.prototype.handleTechClick_ = function handleTechClick_(event) { 22008 if (!isSingleLeftClick(event)) { 22009 return; 22010 } 22011 22012 // When controls are disabled a click should not toggle playback because 22013 // the click is considered a control 22014 if (!this.controls_) { 22015 return; 22016 } 22017 22018 if (this.paused()) { 22019 this.play(); 22020 } else { 22021 this.pause(); 22022 } 22023 }; 22024 22025 /** 22026 * Handle a tap on the media element. It will toggle the user 22027 * activity state, which hides and shows the controls. 22028 * 22029 * @listens Tech#tap 22030 * @private 22031 */ 22032 22033 22034 Player.prototype.handleTechTap_ = function handleTechTap_() { 22035 this.userActive(!this.userActive()); 22036 }; 22037 22038 /** 22039 * Handle touch to start 22040 * 22041 * @listens Tech#touchstart 22042 * @private 22043 */ 22044 22045 22046 Player.prototype.handleTechTouchStart_ = function handleTechTouchStart_() { 22047 this.userWasActive = this.userActive(); 22048 }; 22049 22050 /** 22051 * Handle touch to move 22052 * 22053 * @listens Tech#touchmove 22054 * @private 22055 */ 22056 22057 22058 Player.prototype.handleTechTouchMove_ = function handleTechTouchMove_() { 22059 if (this.userWasActive) { 22060 this.reportUserActivity(); 22061 } 22062 }; 22063 22064 /** 22065 * Handle touch to end 22066 * 22067 * @param {EventTarget~Event} event 22068 * the touchend event that triggered 22069 * this function 22070 * 22071 * @listens Tech#touchend 22072 * @private 22073 */ 22074 22075 22076 Player.prototype.handleTechTouchEnd_ = function handleTechTouchEnd_(event) { 22077 // Stop the mouse events from also happening 22078 event.preventDefault(); 22079 }; 22080 22081 /** 22082 * Fired when the player switches in or out of fullscreen mode 22083 * 22084 * @private 22085 * @listens Player#fullscreenchange 22086 */ 22087 22088 22089 Player.prototype.handleFullscreenChange_ = function handleFullscreenChange_() { 22090 if (this.isFullscreen()) { 22091 this.addClass('vjs-fullscreen'); 22092 } else { 22093 this.removeClass('vjs-fullscreen'); 22094 } 22095 }; 22096 22097 /** 22098 * native click events on the SWF aren't triggered on IE11, Win8.1RT 22099 * use stageclick events triggered from inside the SWF instead 22100 * 22101 * @private 22102 * @listens stageclick 22103 */ 22104 22105 22106 Player.prototype.handleStageClick_ = function handleStageClick_() { 22107 this.reportUserActivity(); 22108 }; 22109 22110 /** 22111 * Handle Tech Fullscreen Change 22112 * 22113 * @param {EventTarget~Event} event 22114 * the fullscreenchange event that triggered this function 22115 * 22116 * @param {Object} data 22117 * the data that was sent with the event 22118 * 22119 * @private 22120 * @listens Tech#fullscreenchange 22121 * @fires Player#fullscreenchange 22122 */ 22123 22124 22125 Player.prototype.handleTechFullscreenChange_ = function handleTechFullscreenChange_(event, data) { 22126 if (data) { 22127 this.isFullscreen(data.isFullscreen); 22128 } 22129 /** 22130 * Fired when going in and out of fullscreen. 22131 * 22132 * @event Player#fullscreenchange 22133 * @type {EventTarget~Event} 22134 */ 22135 this.trigger('fullscreenchange'); 22136 }; 22137 22138 /** 22139 * Fires when an error occurred during the loading of an audio/video. 22140 * 22141 * @private 22142 * @listens Tech#error 22143 */ 22144 22145 22146 Player.prototype.handleTechError_ = function handleTechError_() { 22147 var error = this.tech_.error(); 22148 22149 this.error(error); 22150 }; 22151 22152 /** 22153 * Retrigger the `textdata` event that was triggered by the {@link Tech}. 22154 * 22155 * @fires Player#textdata 22156 * @listens Tech#textdata 22157 * @private 22158 */ 22159 22160 22161 Player.prototype.handleTechTextData_ = function handleTechTextData_() { 22162 var data = null; 22163 22164 if (arguments.length > 1) { 22165 data = arguments[1]; 22166 } 22167 22168 /** 22169 * Fires when we get a textdata event from tech 22170 * 22171 * @event Player#textdata 22172 * @type {EventTarget~Event} 22173 */ 22174 this.trigger('textdata', data); 22175 }; 22176 22177 /** 22178 * Get object for cached values. 22179 * 22180 * @return {Object} 22181 * get the current object cache 22182 */ 22183 22184 22185 Player.prototype.getCache = function getCache() { 22186 return this.cache_; 22187 }; 22188 22189 /** 22190 * Pass values to the playback tech 22191 * 22192 * @param {string} [method] 22193 * the method to call 22194 * 22195 * @param {Object} arg 22196 * the argument to pass 22197 * 22198 * @private 22199 */ 22200 22201 22202 Player.prototype.techCall_ = function techCall_(method, arg) { 22203 // If it's not ready yet, call method when it is 22204 22205 this.ready(function () { 22206 if (method in allowedSetters) { 22207 return set$1(this.middleware_, this.tech_, method, arg); 22208 } 22209 22210 try { 22211 if (this.tech_) { 22212 this.tech_[method](arg); 22213 } 22214 } catch (e) { 22215 log$1(e); 22216 throw e; 22217 } 22218 }, true); 22219 }; 22220 22221 /** 22222 * Get calls can't wait for the tech, and sometimes don't need to. 22223 * 22224 * @param {string} method 22225 * Tech method 22226 * 22227 * @return {Function|undefined} 22228 * the method or undefined 22229 * 22230 * @private 22231 */ 22232 22233 22234 Player.prototype.techGet_ = function techGet_(method) { 22235 if (!this.tech_ || !this.tech_.isReady_) { 22236 return; 22237 } 22238 22239 if (method in allowedGetters) { 22240 return get$1(this.middleware_, this.tech_, method); 22241 } 22242 22243 // Flash likes to die and reload when you hide or reposition it. 22244 // In these cases the object methods go away and we get errors. 22245 // When that happens we'll catch the errors and inform tech that it's not ready any more. 22246 try { 22247 return this.tech_[method](); 22248 } catch (e) { 22249 22250 // When building additional tech libs, an expected method may not be defined yet 22251 if (this.tech_[method] === undefined) { 22252 log$1('Video.js: ' + method + ' method not defined for ' + this.techName_ + ' playback technology.', e); 22253 throw e; 22254 } 22255 22256 // When a method isn't available on the object it throws a TypeError 22257 if (e.name === 'TypeError') { 22258 log$1('Video.js: ' + method + ' unavailable on ' + this.techName_ + ' playback technology element.', e); 22259 this.tech_.isReady_ = false; 22260 throw e; 22261 } 22262 22263 // If error unknown, just log and throw 22264 log$1(e); 22265 throw e; 22266 } 22267 }; 22268 22269 /** 22270 * Attempt to begin playback at the first opportunity. 22271 * 22272 * @return {Promise|undefined} 22273 * Returns a `Promise` only if the browser returns one and the player 22274 * is ready to begin playback. For some browsers and all non-ready 22275 * situations, this will return `undefined`. 22276 */ 22277 22278 22279 Player.prototype.play = function play() { 22280 var _this5 = this; 22281 22282 // If this is called while we have a play queued up on a loadstart, remove 22283 // that listener to avoid getting in a potentially bad state. 22284 if (this.playOnLoadstart_) { 22285 this.off('loadstart', this.playOnLoadstart_); 22286 } 22287 22288 // If the player/tech is not ready, queue up another call to `play()` for 22289 // when it is. This will loop back into this method for another attempt at 22290 // playback when the tech is ready. 22291 if (!this.isReady_) { 22292 22293 // Bail out if we're already waiting for `ready`! 22294 if (this.playWaitingForReady_) { 22295 return; 22296 } 22297 22298 this.playWaitingForReady_ = true; 22299 this.ready(function () { 22300 _this5.playWaitingForReady_ = false; 22301 silencePromise(_this5.play()); 22302 }); 22303 22304 // If the player/tech is ready and we have a source, we can attempt playback. 22305 } else if (!this.changingSrc_ && (this.src() || this.currentSrc())) { 22306 return this.techGet_('play'); 22307 22308 // If the tech is ready, but we do not have a source, we'll need to wait 22309 // for both the `ready` and a `loadstart` when the source is finally 22310 // resolved by middleware and set on the player. 22311 // 22312 // This can happen if `play()` is called while changing sources or before 22313 // one has been set on the player. 22314 } else { 22315 22316 this.playOnLoadstart_ = function () { 22317 _this5.playOnLoadstart_ = null; 22318 silencePromise(_this5.play()); 22319 }; 22320 22321 this.one('loadstart', this.playOnLoadstart_); 22322 } 22323 }; 22324 22325 /** 22326 * Pause the video playback 22327 * 22328 * @return {Player} 22329 * A reference to the player object this function was called on 22330 */ 22331 22332 22333 Player.prototype.pause = function pause() { 22334 this.techCall_('pause'); 22335 }; 22336 22337 /** 22338 * Check if the player is paused or has yet to play 22339 * 22340 * @return {boolean} 22341 * - false: if the media is currently playing 22342 * - true: if media is not currently playing 22343 */ 22344 22345 22346 Player.prototype.paused = function paused() { 22347 // The initial state of paused should be true (in Safari it's actually false) 22348 return this.techGet_('paused') === false ? false : true; 22349 }; 22350 22351 /** 22352 * Get a TimeRange object representing the current ranges of time that the user 22353 * has played. 22354 * 22355 * @return {TimeRange} 22356 * A time range object that represents all the increments of time that have 22357 * been played. 22358 */ 22359 22360 22361 Player.prototype.played = function played() { 22362 return this.techGet_('played') || createTimeRanges(0, 0); 22363 }; 22364 22365 /** 22366 * Returns whether or not the user is "scrubbing". Scrubbing is 22367 * when the user has clicked the progress bar handle and is 22368 * dragging it along the progress bar. 22369 * 22370 * @param {boolean} [isScrubbing] 22371 * wether the user is or is not scrubbing 22372 * 22373 * @return {boolean} 22374 * The value of scrubbing when getting 22375 */ 22376 22377 22378 Player.prototype.scrubbing = function scrubbing(isScrubbing) { 22379 if (typeof isScrubbing === 'undefined') { 22380 return this.scrubbing_; 22381 } 22382 this.scrubbing_ = !!isScrubbing; 22383 22384 if (isScrubbing) { 22385 this.addClass('vjs-scrubbing'); 22386 } else { 22387 this.removeClass('vjs-scrubbing'); 22388 } 22389 }; 22390 22391 /** 22392 * Get or set the current time (in seconds) 22393 * 22394 * @param {number|string} [seconds] 22395 * The time to seek to in seconds 22396 * 22397 * @return {number} 22398 * - the current time in seconds when getting 22399 */ 22400 22401 22402 Player.prototype.currentTime = function currentTime(seconds) { 22403 if (typeof seconds !== 'undefined') { 22404 if (seconds < 0) { 22405 seconds = 0; 22406 } 22407 this.techCall_('setCurrentTime', seconds); 22408 return; 22409 } 22410 22411 // cache last currentTime and return. default to 0 seconds 22412 // 22413 // Caching the currentTime is meant to prevent a massive amount of reads on the tech's 22414 // currentTime when scrubbing, but may not provide much performance benefit afterall. 22415 // Should be tested. Also something has to read the actual current time or the cache will 22416 // never get updated. 22417 this.cache_.currentTime = this.techGet_('currentTime') || 0; 22418 return this.cache_.currentTime; 22419 }; 22420 22421 /** 22422 * Normally gets the length in time of the video in seconds; 22423 * in all but the rarest use cases an argument will NOT be passed to the method 22424 * 22425 * > **NOTE**: The video must have started loading before the duration can be 22426 * known, and in the case of Flash, may not be known until the video starts 22427 * playing. 22428 * 22429 * @fires Player#durationchange 22430 * 22431 * @param {number} [seconds] 22432 * The duration of the video to set in seconds 22433 * 22434 * @return {number} 22435 * - The duration of the video in seconds when getting 22436 */ 22437 22438 22439 Player.prototype.duration = function duration(seconds) { 22440 if (seconds === undefined) { 22441 // return NaN if the duration is not known 22442 return this.cache_.duration !== undefined ? this.cache_.duration : NaN; 22443 } 22444 22445 seconds = parseFloat(seconds); 22446 22447 // Standardize on Inifity for signaling video is live 22448 if (seconds < 0) { 22449 seconds = Infinity; 22450 } 22451 22452 if (seconds !== this.cache_.duration) { 22453 // Cache the last set value for optimized scrubbing (esp. Flash) 22454 this.cache_.duration = seconds; 22455 22456 if (seconds === Infinity) { 22457 this.addClass('vjs-live'); 22458 } else { 22459 this.removeClass('vjs-live'); 22460 } 22461 /** 22462 * @event Player#durationchange 22463 * @type {EventTarget~Event} 22464 */ 22465 this.trigger('durationchange'); 22466 } 22467 }; 22468 22469 /** 22470 * Calculates how much time is left in the video. Not part 22471 * of the native video API. 22472 * 22473 * @return {number} 22474 * The time remaining in seconds 22475 */ 22476 22477 22478 Player.prototype.remainingTime = function remainingTime() { 22479 return this.duration() - this.currentTime(); 22480 }; 22481 22482 /** 22483 * A remaining time function that is intented to be used when 22484 * the time is to be displayed directly to the user. 22485 * 22486 * @return {number} 22487 * The rounded time remaining in seconds 22488 */ 22489 22490 22491 Player.prototype.remainingTimeDisplay = function remainingTimeDisplay() { 22492 return Math.floor(this.duration()) - Math.floor(this.currentTime()); 22493 }; 22494 22495 // 22496 // Kind of like an array of portions of the video that have been downloaded. 22497 22498 /** 22499 * Get a TimeRange object with an array of the times of the video 22500 * that have been downloaded. If you just want the percent of the 22501 * video that's been downloaded, use bufferedPercent. 22502 * 22503 * @see [Buffered Spec]{@link http://dev.w3.org/html5/spec/video.html#dom-media-buffered} 22504 * 22505 * @return {TimeRange} 22506 * A mock TimeRange object (following HTML spec) 22507 */ 22508 22509 22510 Player.prototype.buffered = function buffered() { 22511 var buffered = this.techGet_('buffered'); 22512 22513 if (!buffered || !buffered.length) { 22514 buffered = createTimeRanges(0, 0); 22515 } 22516 22517 return buffered; 22518 }; 22519 22520 /** 22521 * Get the percent (as a decimal) of the video that's been downloaded. 22522 * This method is not a part of the native HTML video API. 22523 * 22524 * @return {number} 22525 * A decimal between 0 and 1 representing the percent 22526 * that is bufferred 0 being 0% and 1 being 100% 22527 */ 22528 22529 22530 Player.prototype.bufferedPercent = function bufferedPercent$$1() { 22531 return bufferedPercent(this.buffered(), this.duration()); 22532 }; 22533 22534 /** 22535 * Get the ending time of the last buffered time range 22536 * This is used in the progress bar to encapsulate all time ranges. 22537 * 22538 * @return {number} 22539 * The end of the last buffered time range 22540 */ 22541 22542 22543 Player.prototype.bufferedEnd = function bufferedEnd() { 22544 var buffered = this.buffered(); 22545 var duration = this.duration(); 22546 var end = buffered.end(buffered.length - 1); 22547 22548 if (end > duration) { 22549 end = duration; 22550 } 22551 22552 return end; 22553 }; 22554 22555 /** 22556 * Get or set the current volume of the media 22557 * 22558 * @param {number} [percentAsDecimal] 22559 * The new volume as a decimal percent: 22560 * - 0 is muted/0%/off 22561 * - 1.0 is 100%/full 22562 * - 0.5 is half volume or 50% 22563 * 22564 * @return {number} 22565 * The current volume as a percent when getting 22566 */ 22567 22568 22569 Player.prototype.volume = function volume(percentAsDecimal) { 22570 var vol = void 0; 22571 22572 if (percentAsDecimal !== undefined) { 22573 // Force value to between 0 and 1 22574 vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal))); 22575 this.cache_.volume = vol; 22576 this.techCall_('setVolume', vol); 22577 22578 if (vol > 0) { 22579 this.lastVolume_(vol); 22580 } 22581 22582 return; 22583 } 22584 22585 // Default to 1 when returning current volume. 22586 vol = parseFloat(this.techGet_('volume')); 22587 return isNaN(vol) ? 1 : vol; 22588 }; 22589 22590 /** 22591 * Get the current muted state, or turn mute on or off 22592 * 22593 * @param {boolean} [muted] 22594 * - true to mute 22595 * - false to unmute 22596 * 22597 * @return {boolean} 22598 * - true if mute is on and getting 22599 * - false if mute is off and getting 22600 */ 22601 22602 22603 Player.prototype.muted = function muted(_muted) { 22604 if (_muted !== undefined) { 22605 this.techCall_('setMuted', _muted); 22606 return; 22607 } 22608 return this.techGet_('muted') || false; 22609 }; 22610 22611 /** 22612 * Get the current defaultMuted state, or turn defaultMuted on or off. defaultMuted 22613 * indicates the state of muted on intial playback. 22614 * 22615 * ```js
22616 * var myPlayer = videojs('some-player-id'); 22617 * 22618 * myPlayer.src("http://www.example.com/path/to/video.mp4"); 22619 * 22620 * // get, should be false 22621 * console.log(myPlayer.defaultMuted()); 22622 * // set to true 22623 * myPlayer.defaultMuted(true); 22624 * // get should be true 22625 * console.log(myPlayer.defaultMuted()); 22626 * ``` 22627 * 22628 * @param {boolean} [defaultMuted] 22629 * - true to mute 22630 * - false to unmute 22631 * 22632 * @return {boolean|Player} 22633 * - true if defaultMuted is on and getting 22634 * - false if defaultMuted is off and getting 22635 * - A reference to the current player when setting 22636 */ 22637 22638 22639 Player.prototype.defaultMuted = function defaultMuted(_defaultMuted) { 22640 if (_defaultMuted !== undefined) { 22641 return this.techCall_('setDefaultMuted', _defaultMuted); 22642 } 22643 return this.techGet_('defaultMuted') || false; 22644 }; 22645 22646 /** 22647 * Get the last volume, or set it 22648 * 22649 * @param {number} [percentAsDecimal] 22650 * The new last volume as a decimal percent: 22651 * - 0 is muted/0%/off 22652 * - 1.0 is 100%/full 22653 * - 0.5 is half volume or 50% 22654 * 22655 * @return {number} 22656 * the current value of lastVolume as a percent when getting 22657 * 22658 * @private 22659 */ 22660 22661 22662 Player.prototype.lastVolume_ = function lastVolume_(percentAsDecimal) { 22663 if (percentAsDecimal !== undefined && percentAsDecimal !== 0) { 22664 this.cache_.lastVolume = percentAsDecimal; 22665 return; 22666 } 22667 return this.cache_.lastVolume; 22668 }; 22669 22670 /** 22671 * Check if current tech can support native fullscreen 22672 * (e.g. with built in controls like iOS, so not our flash swf) 22673 * 22674 * @return {boolean} 22675 * if native fullscreen is supported 22676 */ 22677 22678 22679 Player.prototype.supportsFullScreen = function supportsFullScreen() { 22680 return this.techGet_('supportsFullScreen') || false; 22681 }; 22682 22683 /** 22684 * Check if the player is in fullscreen mode or tell the player that it 22685 * is or is not in fullscreen mode. 22686 * 22687 * > NOTE: As of the latest HTML5 spec, isFullscreen is no longer an official 22688 * property and instead document.fullscreenElement is used. But isFullscreen is 22689 * still a valuable property for internal player workings. 22690 * 22691 * @param {boolean} [isFS] 22692 * Set the players current fullscreen state 22693 * 22694 * @return {boolean} 22695 * - true if fullscreen is on and getting 22696 * - false if fullscreen is off and getting 22697 */ 22698 22699 22700 Player.prototype.isFullscreen = function isFullscreen(isFS) { 22701 if (isFS !== undefined) { 22702 this.isFullscreen_ = !!isFS; 22703 return; 22704 } 22705 return !!this.isFullscreen_; 22706 }; 22707 22708 /** 22709 * Increase the size of the video to full screen 22710 * In some browsers, full screen is not supported natively, so it enters 22711 * "full window mode", where the video fills the browser window. 22712 * In browsers and devices that support native full screen, sometimes the 22713 * browser's default controls will be shown, and not the Video.js custom skin. 22714 * This includes most mobile devices (iOS, Android) and older versions of 22715 * Safari. 22716 * 22717 * @fires Player#fullscreenchange 22718 */ 22719 22720 22721 Player.prototype.requestFullscreen = function requestFullscreen() { 22722 var fsApi = FullscreenApi; 22723 22724 this.isFullscreen(true); 22725 22726 if (fsApi.requestFullscreen) { 22727 // the browser supports going fullscreen at the element level so we can 22728 // take the controls fullscreen as well as the video 22729 22730 // Trigger fullscreenchange event after change 22731 // We have to specifically add this each time, and remove 22732 // when canceling fullscreen. Otherwise if there's multiple 22733 // players on a page, they would all be reacting to the same fullscreen 22734 // events 22735 on(document_1, fsApi.fullscreenchange, bind(this, function documentFullscreenChange(e) { 22736 this.isFullscreen(document_1[fsApi.fullscreenElement]); 22737 22738 // If cancelling fullscreen, remove event listener. 22739 if (this.isFullscreen() === false) { 22740 off(document_1, fsApi.fullscreenchange, documentFullscreenChange); 22741 } 22742 /** 22743 * @event Player#fullscreenchange 22744 * @type {EventTarget~Event} 22745 */ 22746 this.trigger('fullscreenchange'); 22747 })); 22748 22749 this.el_[fsApi.requestFullscreen](); 22750 } else if (this.tech_.supportsFullScreen()) { 22751 // we can't take the video.js controls fullscreen but we can go fullscreen 22752 // with native controls 22753 this.techCall_('enterFullScreen'); 22754 } else { 22755 // fullscreen isn't supported so we'll just stretch the video element to 22756 // fill the viewport 22757 this.enterFullWindow(); 22758 /** 22759 * @event Player#fullscreenchange 22760 * @type {EventTarget~Event} 22761 */ 22762 this.trigger('fullscreenchange'); 22763 } 22764 }; 22765 22766 /** 22767 * Return the video to its normal size after having been in full screen mode 22768 * 22769 * @fires Player#fullscreenchange 22770 */ 22771 22772 22773 Player.prototype.exitFullscreen = function exitFullscreen() { 22774 var fsApi = FullscreenApi; 22775 22776 this.isFullscreen(false); 22777 22778 // Check for browser element fullscreen support 22779 if (fsApi.requestFullscreen) { 22780 document_1[fsApi.exitFullscreen](); 22781 } else if (this.tech_.supportsFullScreen()) { 22782 this.techCall_('exitFullScreen'); 22783 } else { 22784 this.exitFullWindow(); 22785 /** 22786 * @event Player#fullscreenchange 22787 * @type {EventTarget~Event} 22788 */ 22789 this.trigger('fullscreenchange'); 22790 } 22791 }; 22792 22793 /** 22794 * When fullscreen isn't supported we can stretch the 22795 * video container to as wide as the browser will let us. 22796 * 22797 * @fires Player#enterFullWindow 22798 */ 22799 22800 22801 Player.prototype.enterFullWindow = function enterFullWindow() { 22802 this.isFullWindow = true; 22803 22804 // Storing original doc overflow value to return to when fullscreen is off 22805 this.docOrigOverflow = document_1.documentElement.style.overflow; 22806 22807 // Add listener for esc key to exit fullscreen 22808 on(document_1, 'keydown', bind(this, this.fullWindowOnEscKey)); 22809 22810 // Hide any scroll bars 22811 document_1.documentElement.style.overflow = 'hidden'; 22812 22813 // Apply fullscreen styles 22814 addClass(document_1.body, 'vjs-full-window'); 22815 22816 /** 22817 * @event Player#enterFullWindow 22818 * @type {EventTarget~Event} 22819 */ 22820 this.trigger('enterFullWindow'); 22821 }; 22822 22823 /** 22824 * Check for call to either exit full window or 22825 * full screen on ESC key 22826 * 22827 * @param {string} event 22828 * Event to check for key press 22829 */ 22830 22831 22832 Player.prototype.fullWindowOnEscKey = function fullWindowOnEscKey(event) { 22833 if (event.keyCode === 27) { 22834 if (this.isFullscreen() === true) { 22835 this.exitFullscreen(); 22836 } else { 22837 this.exitFullWindow(); 22838 } 22839 } 22840 }; 22841 22842 /** 22843 * Exit full window 22844 * 22845 * @fires Player#exitFullWindow 22846 */ 22847 22848 22849 Player.prototype.exitFullWindow = function exitFullWindow() { 22850 this.isFullWindow = false;
vendor: 4,466 bytes, lines 22851-22999
22851 off(document_1, 'keydown', this.fullWindowOnEscKey); 22852 22853 // Unhide scroll bars. 22854 document_1.documentElement.style.overflow = this.docOrigOverflow; 22855 22856 // Remove fullscreen styles 22857 removeClass(document_1.body, 'vjs-full-window'); 22858 22859 // Resize the box, controller, and poster to original sizes 22860 // this.positionAll(); 22861 /** 22862 * @event Player#exitFullWindow 22863 * @type {EventTarget~Event} 22864 */ 22865 this.trigger('exitFullWindow'); 22866 }; 22867 22868 /** 22869 * Check whether the player can play a given mimetype 22870 * 22871 * @see https://www.w3.org/TR/2011/WD-html5-20110113/video.html#dom-navigator-canplaytype 22872 * 22873 * @param {string} type 22874 * The mimetype to check 22875 * 22876 * @return {string} 22877 * 'probably', 'maybe', or '' (empty string) 22878 */ 22879 22880 22881 Player.prototype.canPlayType = function canPlayType(type) { 22882 var can = void 0; 22883 22884 // Loop through each playback technology in the options order 22885 for (var i = 0, j = this.options_.techOrder; i < j.length; i++) { 22886 var techName = j[i]; 22887 var tech = Tech.getTech(techName); 22888 22889 // Support old behavior of techs being registered as components. 22890 // Remove once that deprecated behavior is removed. 22891 if (!tech) { 22892 tech = Component.getComponent(techName); 22893 } 22894 22895 // Check if the current tech is defined before continuing 22896 if (!tech) { 22897 log$1.error('The "' + techName + '" tech is undefined. Skipped browser support check for that tech.'); 22898 continue; 22899 } 22900 22901 // Check if the browser supports this technology 22902 if (tech.isSupported()) { 22903 can = tech.canPlayType(type); 22904 22905 if (can) { 22906 return can; 22907 } 22908 } 22909 } 22910 22911 return ''; 22912 }; 22913 22914 /** 22915 * Select source based on tech-order or source-order 22916 * Uses source-order selection if `options.sourceOrder` is truthy. Otherwise, 22917 * defaults to tech-order selection 22918 * 22919 * @param {Array} sources 22920 * The sources for a media asset 22921 * 22922 * @return {Object|boolean} 22923 * Object of source and tech order or false 22924 */ 22925 22926 22927 Player.prototype.selectSource = function selectSource(sources) { 22928 var _this6 = this; 22929 22930 // Get only the techs specified in `techOrder` that exist and are supported by the 22931 // current platform 22932 var techs = this.options_.techOrder.map(function (techName) { 22933 return [techName, Tech.getTech(techName)]; 22934 }).filter(function (_ref) { 22935 var techName = _ref[0], 22936 tech = _ref[1]; 22937 22938 // Check if the current tech is defined before continuing 22939 if (tech) { 22940 // Check if the browser supports this technology 22941 return tech.isSupported(); 22942 } 22943 22944 log$1.error('The "' + techName + '" tech is undefined. Skipped browser support check for that tech.'); 22945 return false; 22946 }); 22947 22948 // Iterate over each `innerArray` element once per `outerArray` element and execute 22949 // `tester` with both. If `tester` returns a non-falsy value, exit early and return 22950 // that value. 22951 var findFirstPassingTechSourcePair = function findFirstPassingTechSourcePair(outerArray, innerArray, tester) { 22952 var found = void 0; 22953 22954 outerArray.some(function (outerChoice) { 22955 return innerArray.some(function (innerChoice) { 22956 found = tester(outerChoice, innerChoice); 22957 22958 if (found) { 22959 return true; 22960 } 22961 }); 22962 }); 22963 22964 return found; 22965 }; 22966 22967 var foundSourceAndTech = void 0; 22968 var flip = function flip(fn) { 22969 return function (a, b) { 22970 return fn(b, a); 22971 }; 22972 }; 22973 var finder = function finder(_ref2, source) { 22974 var techName = _ref2[0], 22975 tech = _ref2[1]; 22976 22977 if (tech.canPlaySource(source, _this6.options_[techName.toLowerCase()])) { 22978 return { source: source, tech: techName }; 22979 } 22980 }; 22981 22982 // Depending on the truthiness of `options.sourceOrder`, we swap the order of techs and sources 22983 // to select from them based on their priority. 22984 if (this.options_.sourceOrder) { 22985 // Source-first ordering 22986 foundSourceAndTech = findFirstPassingTechSourcePair(sources, techs, flip(finder)); 22987 } else { 22988 // Tech-first ordering 22989 foundSourceAndTech = findFirstPassingTechSourcePair(techs, sources, finder); 22990 } 22991 22992 return foundSourceAndTech || false; 22993 }; 22994 22995 /** 22996 * Get or set the video source. 22997 * 22998 * @param {Tech~SourceObject|Tech~SourceObject[]|string} [source] 22999 * A SourceObject, an array of SourceObjects, or a string referencing
23000 * a URL to a media source. It is _highly recommended_ that an object 23001 * or array of objects is used here, so that source selection 23002 * algorithms can take the `type` into account. 23003 * 23004 * If not provided, this method acts as a getter. 23005 * 23006 * @return {string|undefined} 23007 * If the `source` argument is missing, returns the current source 23008 * URL. Otherwise, returns nothing/undefined. 23009 */ 23010 23011 23012 Player.prototype.src = function src(source) { 23013 var _this7 = this; 23014 23015 // getter usage 23016 if (typeof source === 'undefined') { 23017 return this.cache_.src || ''; 23018 } 23019 // filter out invalid sources and turn our source into 23020 // an array of source objects 23021 var sources = filterSource(source); 23022 23023 // if a source was passed in then it is invalid because 23024 // it was filtered to a zero length Array. So we have to 23025 // show an error 23026 if (!sources.length) { 23027 this.setTimeout(function () { 23028 this.error({ code: 4, message: this.localize(this.options_.notSupportedMessage) }); 23029 }, 0); 23030 return; 23031 } 23032 23033 // intial sources 23034 this.cache_.sources = sources; 23035 this.changingSrc_ = true; 23036 23037 // intial source 23038 this.cache_.source = sources[0]; 23039 23040 // middlewareSource is the source after it has been changed by middleware 23041 setSource(this, sources[0], function (middlewareSource, mws) { 23042 _this7.middleware_ = mws; 23043 23044 var err = _this7.src_(middlewareSource); 23045 23046 if (err) { 23047 if (sources.length > 1) { 23048 return _this7.src(sources.slice(1)); 23049 } 23050 23051 // We need to wrap this in a timeout to give folks a chance to add error event handlers 23052 _this7.setTimeout(function () { 23053 this.error({ code: 4, message: this.localize(this.options_.notSupportedMessage) }); 23054 }, 0); 23055 23056 // we could not find an appropriate tech, but let's still notify the delegate that this is it 23057 // this needs a better comment about why this is needed 23058 _this7.triggerReady(); 23059 23060 return; 23061 } 23062 23063 _this7.changingSrc_ = false; 23064 // video element listed source 23065 _this7.cache_.src = middlewareSource.src; 23066 23067 setTech(mws, _this7.tech_); 23068 }); 23069 }; 23070 23071 /** 23072 * Set the source object on the tech, returns a boolean that indicates wether 23073 * there is a tech that can play the source or not 23074 * 23075 * @param {Tech~SourceObject} source 23076 * The source object to set on the Tech 23077 * 23078 * @return {Boolean} 23079 * - True if there is no Tech to playback this source 23080 * - False otherwise 23081 * 23082 * @private 23083 */ 23084 23085 23086 Player.prototype.src_ = function src_(source) { 23087 var sourceTech = this.selectSource([source]); 23088 23089 if (!sourceTech) { 23090 return true; 23091 } 23092 23093 if (!titleCaseEquals(sourceTech.tech, this.techName_)) { 23094 this.changingSrc_ = true; 23095 23096 // load this technology with the chosen source 23097 this.loadTech_(sourceTech.tech, sourceTech.source); 23098 return false; 23099 } 23100 23101 // wait until the tech is ready to set the source 23102 this.ready(function () { 23103 23104 // The setSource tech method was added with source handlers 23105 // so older techs won't support it 23106 // We need to check the direct prototype for the case where subclasses 23107 // of the tech do not support source handlers 23108 if (this.tech_.constructor.prototype.hasOwnProperty('setSource')) { 23109 this.techCall_('setSource', source); 23110 } else { 23111 this.techCall_('src', source.src); 23112 } 23113 23114 if (this.options_.preload === 'auto') { 23115 this.load(); 23116 } 23117 23118 // Set the source synchronously if possible (#2326) 23119 }, true); 23120 23121 return false; 23122 }; 23123 23124 /** 23125 * Begin loading the src data. 23126 */ 23127 23128 23129 Player.prototype.load = function load() { 23130 this.techCall_('load'); 23131 }; 23132 23133 /** 23134 * Reset the player. Loads the first tech in the techOrder, 23135 * and calls `reset` on the tech`. 23136 */ 23137 23138 23139 Player.prototype.reset = function reset() { 23140 this.loadTech_(this.options_.techOrder[0], null); 23141 this.techCall_('reset'); 23142 }; 23143 23144 /** 23145 * Returns all of the current source objects. 23146 * 23147 * @return {Tech~SourceObject[]} 23148 * The current source objects 23149 */ 23150 23151 23152 Player.prototype.currentSources = function currentSources() { 23153 var source = this.currentSource(); 23154 var sources = []; 23155 23156 // assume `{}` or `{ src }` 23157 if (Object.keys(source).length !== 0) { 23158 sources.push(source); 23159 } 23160 23161 return this.cache_.sources || sources; 23162 }; 23163 23164 /** 23165 * Returns the current source object. 23166 * 23167 * @return {Tech~SourceObject} 23168 * The current source object 23169 */ 23170 23171 23172 Player.prototype.currentSource = function currentSource() { 23173 return this.cache_.source || {}; 23174 }; 23175 23176 /** 23177 * Returns the fully qualified URL of the current source value e.g. http://mysite.com/video.mp4 23178 * Can be used in conjuction with `currentType` to assist in rebuilding the current source object. 23179 * 23180 * @return {string} 23181 * The current source 23182 */ 23183 23184 23185 Player.prototype.currentSrc = function currentSrc() { 23186 return this.currentSource() && this.currentSource().src || ''; 23187 }; 23188 23189 /** 23190 * Get the current source type e.g. video/mp4 23191 * This can allow you rebuild the current source object so that you could load the same 23192 * source and tech later 23193 * 23194 * @return {string} 23195 * The source MIME type 23196 */ 23197 23198 23199 Player.prototype.currentType = function currentType() { 23200 return this.currentSource() && this.currentSource().type || ''; 23201 }; 23202 23203 /** 23204 * Get or set the preload attribute 23205 * 23206 * @param {boolean} [value] 23207 * - true means that we should preload 23208 * - false maens that we should not preload 23209 * 23210 * @return {string} 23211 * The preload attribute value when getting 23212 */ 23213 23214 23215 Player.prototype.preload = function preload(value) { 23216 if (value !== undefined) { 23217 this.techCall_('setPreload', value); 23218 this.options_.preload = value; 23219 return; 23220 } 23221 return this.techGet_('preload'); 23222 }; 23223 23224 /** 23225 * Get or set the autoplay attribute. 23226 * 23227 * @param {boolean} [value] 23228 * - true means that we should autoplay 23229 * - false means that we should not autoplay 23230 * 23231 * @return {string} 23232 * The current value of autoplay when getting 23233 */ 23234 23235 23236 Player.prototype.autoplay = function autoplay(value) { 23237 if (value !== undefined) { 23238 this.techCall_('setAutoplay', value); 23239 this.options_.autoplay = value; 23240 this.ready(this.forceAutoplayInChrome_); 23241 return; 23242 } 23243 return this.techGet_('autoplay', value); 23244 }; 23245 23246 /** 23247 * chrome started pausing the video when moving in the DOM 23248 * causing autoplay to not continue due to how Video.js functions. 23249 * See #4720 for more info. 23250 * 23251 * @private 23252 */ 23253 23254 23255 Player.prototype.forceAutoplayInChrome_ = function forceAutoplayInChrome_() { 23256 if (this.paused() && ( 23257 // read from the video element or options 23258 this.autoplay() || this.options_.autoplay) && 23259 // only target desktop chrome 23260 IS_CHROME && !IS_ANDROID) { 23261 this.play(); 23262 } 23263 }; 23264 23265 /** 23266 * Set or unset the playsinline attribute. 23267 * Playsinline tells the browser that non-fullscreen playback is preferred. 23268 * 23269 * @param {boolean} [value] 23270 * - true means that we should try to play inline by default 23271 * - false means that we should use the browser's default playback mode, 23272 * which in most cases is inline. iOS Safari is a notable exception 23273 * and plays fullscreen by default. 23274 * 23275 * @return {string|Player} 23276 * - the current value of playsinline 23277 * - the player when setting 23278 * 23279 * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline} 23280 */ 23281 23282 23283 Player.prototype.playsinline = function playsinline(value) { 23284 if (value !== undefined) { 23285 this.techCall_('setPlaysinline', value); 23286 this.options_.playsinline = value; 23287 return this; 23288 } 23289 return this.techGet_('playsinline'); 23290 }; 23291 23292 /** 23293 * Get or set the loop attribute on the video element. 23294 * 23295 * @param {boolean} [value] 23296 * - true means that we should loop the video 23297 * - false means that we should not loop the video 23298 * 23299 * @return {string} 23300 * The current value of loop when getting 23301 */ 23302 23303 23304 Player.prototype.loop = function loop(value) { 23305 if (value !== undefined) { 23306 this.techCall_('setLoop', value); 23307 this.options_.loop = value; 23308 return; 23309 } 23310 return this.techGet_('loop'); 23311 }; 23312 23313 /** 23314 * Get or set the poster image source url 23315 * 23316 * @fires Player#posterchange 23317 * 23318 * @param {string} [src] 23319 * Poster image source URL 23320 * 23321 * @return {string} 23322 * The current value of poster when getting 23323 */ 23324 23325 23326 Player.prototype.poster = function poster(src) { 23327 if (src === undefined) { 23328 return this.poster_; 23329 } 23330 23331 // The correct way to remove a poster is to set as an empty string 23332 // other falsey values will throw errors 23333 if (!src) { 23334 src = ''; 23335 } 23336 23337 // update the internal poster variable 23338 this.poster_ = src; 23339 23340 // update the tech's poster 23341 this.techCall_('setPoster', src); 23342 23343 // alert components that the poster has been set 23344 /** 23345 * This event fires when the poster image is changed on the player. 23346 * 23347 * @event Player#posterchange 23348 * @type {EventTarget~Event} 23349 */ 23350 this.trigger('posterchange'); 23351 }; 23352 23353 /** 23354 * Some techs (e.g. YouTube) can provide a poster source in an 23355 * asynchronous way. We want the poster component to use this 23356 * poster source so that it covers up the tech's controls. 23357 * (YouTube's play button). However we only want to use this 23358 * source if the player user hasn't set a poster through 23359 * the normal APIs. 23360 * 23361 * @fires Player#posterchange 23362 * @listens Tech#posterchange 23363 * @private 23364 */ 23365 23366 23367 Player.prototype.handleTechPosterChange_ = function handleTechPosterChange_() { 23368 if (!this.poster_ && this.tech_ && this.tech_.poster) { 23369 this.poster_ = this.tech_.poster() || ''; 23370 23371 // Let components know the poster has changed 23372 this.trigger('posterchange'); 23373 } 23374 }; 23375 23376 /** 23377 * Get or set whether or not the controls are showing. 23378 * 23379 * @fires Player#controlsenabled 23380 * 23381 * @param {boolean} [bool] 23382 * - true to turn controls on 23383 * - false to turn controls off 23384 * 23385 * @return {boolean} 23386 * The current value of controls when getting 23387 */ 23388 23389 23390 Player.prototype.controls = function controls(bool) { 23391 if (bool === undefined) { 23392 return !!this.controls_; 23393 } 23394 23395 bool = !!bool; 23396 23397 // Don't trigger a change event unless it actually changed 23398 if (this.controls_ === bool) { 23399 return; 23400 } 23401 23402 this.controls_ = bool; 23403 23404 if (this.usingNativeControls()) { 23405 this.techCall_('setControls', bool); 23406 } 23407 23408 if (this.controls_) { 23409 this.removeClass('vjs-controls-disabled'); 23410 this.addClass('vjs-controls-enabled'); 23411 /** 23412 * @event Player#controlsenabled 23413 * @type {EventTarget~Event} 23414 */ 23415 this.trigger('controlsenabled'); 23416 if (!this.usingNativeControls()) {
vendor: 16,856 bytes, lines 23417-24006
23417 this.addTechControlsListeners_(); 23418 } 23419 } else { 23420 this.removeClass('vjs-controls-enabled'); 23421 this.addClass('vjs-controls-disabled'); 23422 /** 23423 * @event Player#controlsdisabled 23424 * @type {EventTarget~Event} 23425 */ 23426 this.trigger('controlsdisabled'); 23427 if (!this.usingNativeControls()) { 23428 this.removeTechControlsListeners_(); 23429 } 23430 } 23431 }; 23432 23433 /** 23434 * Toggle native controls on/off. Native controls are the controls built into 23435 * devices (e.g. default iPhone controls), Flash, or other techs 23436 * (e.g. Vimeo Controls) 23437 * **This should only be set by the current tech, because only the tech knows 23438 * if it can support native controls** 23439 * 23440 * @fires Player#usingnativecontrols 23441 * @fires Player#usingcustomcontrols 23442 * 23443 * @param {boolean} [bool] 23444 * - true to turn native controls on 23445 * - false to turn native controls off 23446 * 23447 * @return {boolean} 23448 * The current value of native controls when getting 23449 */ 23450 23451 23452 Player.prototype.usingNativeControls = function usingNativeControls(bool) { 23453 if (bool === undefined) { 23454 return !!this.usingNativeControls_; 23455 } 23456 23457 bool = !!bool; 23458 23459 // Don't trigger a change event unless it actually changed 23460 if (this.usingNativeControls_ === bool) { 23461 return; 23462 } 23463 23464 this.usingNativeControls_ = bool; 23465 23466 if (this.usingNativeControls_) { 23467 this.addClass('vjs-using-native-controls'); 23468 23469 /** 23470 * player is using the native device controls 23471 * 23472 * @event Player#usingnativecontrols 23473 * @type {EventTarget~Event} 23474 */ 23475 this.trigger('usingnativecontrols'); 23476 } else { 23477 this.removeClass('vjs-using-native-controls'); 23478 23479 /** 23480 * player is using the custom HTML controls 23481 * 23482 * @event Player#usingcustomcontrols 23483 * @type {EventTarget~Event} 23484 */ 23485 this.trigger('usingcustomcontrols'); 23486 } 23487 }; 23488 23489 /** 23490 * Set or get the current MediaError 23491 * 23492 * @fires Player#error 23493 * 23494 * @param {MediaError|string|number} [err] 23495 * A MediaError or a string/number to be turned 23496 * into a MediaError 23497 * 23498 * @return {MediaError|null} 23499 * The current MediaError when getting (or null) 23500 */ 23501 23502 23503 Player.prototype.error = function error(err) { 23504 if (err === undefined) { 23505 return this.error_ || null; 23506 } 23507 23508 // restoring to default 23509 if (err === null) { 23510 this.error_ = err; 23511 this.removeClass('vjs-error'); 23512 if (this.errorDisplay) { 23513 this.errorDisplay.close(); 23514 } 23515 return; 23516 } 23517 23518 this.error_ = new MediaError(err); 23519 23520 // add the vjs-error classname to the player 23521 this.addClass('vjs-error'); 23522 23523 // log the name of the error type and any message 23524 // ie8 just logs "[object object]" if you just log the error object 23525 log$1.error('(CODE:' + this.error_.code + ' ' + MediaError.errorTypes[this.error_.code] + ')', this.error_.message, this.error_); 23526 23527 /** 23528 * @event Player#error 23529 * @type {EventTarget~Event} 23530 */ 23531 this.trigger('error'); 23532 23533 return; 23534 }; 23535 23536 /** 23537 * Report user activity 23538 * 23539 * @param {Object} event 23540 * Event object 23541 */ 23542 23543 23544 Player.prototype.reportUserActivity = function reportUserActivity(event) { 23545 this.userActivity_ = true; 23546 }; 23547 23548 /** 23549 * Get/set if user is active 23550 * 23551 * @fires Player#useractive 23552 * @fires Player#userinactive 23553 * 23554 * @param {boolean} [bool] 23555 * - true if the user is active 23556 * - false if the user is inactive 23557 * 23558 * @return {boolean} 23559 * The current value of userActive when getting 23560 */ 23561 23562 23563 Player.prototype.userActive = function userActive(bool) { 23564 if (bool === undefined) { 23565 return this.userActive_; 23566 } 23567 23568 bool = !!bool; 23569 23570 if (bool === this.userActive_) { 23571 return; 23572 } 23573 23574 this.userActive_ = bool; 23575 23576 if (this.userActive_) { 23577 this.userActivity_ = true; 23578 this.removeClass('vjs-user-inactive'); 23579 this.addClass('vjs-user-active'); 23580 /** 23581 * @event Player#useractive 23582 * @type {EventTarget~Event} 23583 */ 23584 this.trigger('useractive'); 23585 return; 23586 } 23587 23588 // Chrome/Safari/IE have bugs where when you change the cursor it can 23589 // trigger a mousemove event. This causes an issue when you're hiding 23590 // the cursor when the user is inactive, and a mousemove signals user 23591 // activity. Making it impossible to go into inactive mode. Specifically 23592 // this happens in fullscreen when we really need to hide the cursor. 23593 // 23594 // When this gets resolved in ALL browsers it can be removed 23595 // https://code.google.com/p/chromium/issues/detail?id=103041 23596 if (this.tech_) { 23597 this.tech_.one('mousemove', function (e) { 23598 e.stopPropagation(); 23599 e.preventDefault(); 23600 }); 23601 } 23602 23603 this.userActivity_ = false; 23604 this.removeClass('vjs-user-active'); 23605 this.addClass('vjs-user-inactive'); 23606 /** 23607 * @event Player#userinactive 23608 * @type {EventTarget~Event} 23609 */ 23610 this.trigger('userinactive'); 23611 }; 23612 23613 /** 23614 * Listen for user activity based on timeout value 23615 * 23616 * @private 23617 */ 23618 23619 23620 Player.prototype.listenForUserActivity_ = function listenForUserActivity_() { 23621 var mouseInProgress = void 0; 23622 var lastMoveX = void 0; 23623 var lastMoveY = void 0; 23624 var handleActivity = bind(this, this.reportUserActivity); 23625 23626 var handleMouseMove = function handleMouseMove(e) { 23627 // #1068 - Prevent mousemove spamming 23628 // Chrome Bug: https://code.google.com/p/chromium/issues/detail?id=366970 23629 if (e.screenX !== lastMoveX || e.screenY !== lastMoveY) { 23630 lastMoveX = e.screenX; 23631 lastMoveY = e.screenY; 23632 handleActivity(); 23633 } 23634 }; 23635 23636 var handleMouseDown = function handleMouseDown() { 23637 handleActivity(); 23638 // For as long as the they are touching the device or have their mouse down, 23639 // we consider them active even if they're not moving their finger or mouse. 23640 // So we want to continue to update that they are active 23641 this.clearInterval(mouseInProgress); 23642 // Setting userActivity=true now and setting the interval to the same time 23643 // as the activityCheck interval (250) should ensure we never miss the 23644 // next activityCheck 23645 mouseInProgress = this.setInterval(handleActivity, 250); 23646 }; 23647 23648 var handleMouseUp = function handleMouseUp(event) { 23649 handleActivity(); 23650 // Stop the interval that maintains activity if the mouse/touch is down 23651 this.clearInterval(mouseInProgress); 23652 }; 23653 23654 // Any mouse movement will be considered user activity 23655 this.on('mousedown', handleMouseDown); 23656 this.on('mousemove', handleMouseMove); 23657 this.on('mouseup', handleMouseUp); 23658 23659 // Listen for keyboard navigation 23660 // Shouldn't need to use inProgress interval because of key repeat 23661 this.on('keydown', handleActivity); 23662 this.on('keyup', handleActivity); 23663 23664 // Run an interval every 250 milliseconds instead of stuffing everything into 23665 // the mousemove/touchmove function itself, to prevent performance degradation. 23666 // `this.reportUserActivity` simply sets this.userActivity_ to true, which 23667 // then gets picked up by this loop 23668 // http://ejohn.org/blog/learning-from-twitter/ 23669 var inactivityTimeout = void 0; 23670 23671 this.setInterval(function () { 23672 // Check to see if mouse/touch activity has happened 23673 if (!this.userActivity_) { 23674 return; 23675 } 23676 23677 // Reset the activity tracker 23678 this.userActivity_ = false; 23679 23680 // If the user state was inactive, set the state to active 23681 this.userActive(true); 23682 23683 // Clear any existing inactivity timeout to start the timer over 23684 this.clearTimeout(inactivityTimeout); 23685 23686 var timeout = this.options_.inactivityTimeout; 23687 23688 if (timeout <= 0) { 23689 return; 23690 } 23691 23692 // In <timeout> milliseconds, if no more activity has occurred the 23693 // user will be considered inactive 23694 inactivityTimeout = this.setTimeout(function () { 23695 // Protect against the case where the inactivityTimeout can trigger just 23696 // before the next user activity is picked up by the activity check loop 23697 // causing a flicker 23698 if (!this.userActivity_) { 23699 this.userActive(false); 23700 } 23701 }, timeout); 23702 }, 250); 23703 }; 23704 23705 /** 23706 * Gets or sets the current playback rate. A playback rate of 23707 * 1.0 represents normal speed and 0.5 would indicate half-speed 23708 * playback, for instance. 23709 * 23710 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-playbackrate 23711 * 23712 * @param {number} [rate] 23713 * New playback rate to set. 23714 * 23715 * @return {number} 23716 * The current playback rate when getting or 1.0 23717 */ 23718 23719 23720 Player.prototype.playbackRate = function playbackRate(rate) { 23721 if (rate !== undefined) { 23722 this.techCall_('setPlaybackRate', rate); 23723 return; 23724 } 23725 23726 if (this.tech_ && this.tech_.featuresPlaybackRate) { 23727 return this.techGet_('playbackRate'); 23728 } 23729 return 1.0; 23730 }; 23731 23732 /** 23733 * Gets or sets the current default playback rate. A default playback rate of 23734 * 1.0 represents normal speed and 0.5 would indicate half-speed playback, for instance. 23735 * defaultPlaybackRate will only represent what the intial playbackRate of a video was, not 23736 * not the current playbackRate. 23737 * 23738 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-defaultplaybackrate 23739 * 23740 * @param {number} [rate] 23741 * New default playback rate to set. 23742 * 23743 * @return {number|Player} 23744 * - The default playback rate when getting or 1.0 23745 * - the player when setting 23746 */ 23747 23748 23749 Player.prototype.defaultPlaybackRate = function defaultPlaybackRate(rate) { 23750 if (rate !== undefined) { 23751 return this.techCall_('setDefaultPlaybackRate', rate); 23752 } 23753 23754 if (this.tech_ && this.tech_.featuresPlaybackRate) { 23755 return this.techGet_('defaultPlaybackRate'); 23756 } 23757 return 1.0; 23758 }; 23759 23760 /** 23761 * Gets or sets the audio flag 23762 * 23763 * @param {boolean} bool 23764 * - true signals that this is an audio player 23765 * - false signals that this is not an audio player 23766 * 23767 * @return {boolean} 23768 * The current value of isAudio when getting 23769 */ 23770 23771 23772 Player.prototype.isAudio = function isAudio(bool) { 23773 if (bool !== undefined) { 23774 this.isAudio_ = !!bool; 23775 return; 23776 } 23777 23778 return !!this.isAudio_; 23779 }; 23780 23781 /** 23782 * A helper method for adding a {@link TextTrack} to our 23783 * {@link TextTrackList}. 23784 * 23785 * In addition to the W3C settings we allow adding additional info through options. 23786 * 23787 * @see http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-addtexttrack 23788 * 23789 * @param {string} [kind] 23790 * the kind of TextTrack you are adding 23791 * 23792 * @param {string} [label] 23793 * the label to give the TextTrack label 23794 * 23795 * @param {string} [language] 23796 * the language to set on the TextTrack 23797 * 23798 * @return {TextTrack|undefined} 23799 * the TextTrack that was added or undefined 23800 * if there is no tech 23801 */ 23802 23803 23804 Player.prototype.addTextTrack = function addTextTrack(kind, label, language) { 23805 if (this.tech_) { 23806 return this.tech_.addTextTrack(kind, label, language); 23807 } 23808 }; 23809 23810 /** 23811 * Create a remote {@link TextTrack} and an {@link HTMLTrackElement}. It will 23812 * automatically removed from the video element whenever the source changes, unless 23813 * manualCleanup is set to false. 23814 * 23815 * @param {Object} options 23816 * Options to pass to {@link HTMLTrackElement} during creation. See 23817 * {@link HTMLTrackElement} for object properties that you should use. 23818 * 23819 * @param {boolean} [manualCleanup=true] if set to false, the TextTrack will be 23820 * 23821 * @return {HtmlTrackElement} 23822 * the HTMLTrackElement that was created and added 23823 * to the HtmlTrackElementList and the remote 23824 * TextTrackList 23825 * 23826 * @deprecated The default value of the "manualCleanup" parameter will default 23827 * to "false" in upcoming versions of Video.js 23828 */ 23829 23830 23831 Player.prototype.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) { 23832 if (this.tech_) { 23833 return this.tech_.addRemoteTextTrack(options, manualCleanup); 23834 } 23835 }; 23836 23837 /** 23838 * Remove a remote {@link TextTrack} from the respective 23839 * {@link TextTrackList} and {@link HtmlTrackElementList}. 23840 * 23841 * @param {Object} track 23842 * Remote {@link TextTrack} to remove 23843 * 23844 * @return {undefined} 23845 * does not return anything 23846 */ 23847 23848 23849 Player.prototype.removeRemoteTextTrack = function removeRemoteTextTrack() { 23850 var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, 23851 _ref3$track = _ref3.track, 23852 track = _ref3$track === undefined ? arguments[0] : _ref3$track; 23853 23854 // destructure the input into an object with a track argument, defaulting to arguments[0] 23855 // default the whole argument to an empty object if nothing was passed in 23856 23857 if (this.tech_) { 23858 return this.tech_.removeRemoteTextTrack(track); 23859 } 23860 }; 23861 23862 /** 23863 * Gets available media playback quality metrics as specified by the W3C's Media 23864 * Playback Quality API. 23865 * 23866 * @see [Spec]{@link https://wicg.github.io/media-playback-quality} 23867 * 23868 * @return {Object|undefined} 23869 * An object with supported media playback quality metrics or undefined if there 23870 * is no tech or the tech does not support it. 23871 */ 23872 23873 23874 Player.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() { 23875 return this.techGet_('getVideoPlaybackQuality'); 23876 }; 23877 23878 /** 23879 * Get video width 23880 * 23881 * @return {number} 23882 * current video width 23883 */ 23884 23885 23886 Player.prototype.videoWidth = function videoWidth() { 23887 return this.tech_ && this.tech_.videoWidth && this.tech_.videoWidth() || 0; 23888 }; 23889 23890 /** 23891 * Get video height 23892 * 23893 * @return {number} 23894 * current video height 23895 */ 23896 23897 23898 Player.prototype.videoHeight = function videoHeight() { 23899 return this.tech_ && this.tech_.videoHeight && this.tech_.videoHeight() || 0; 23900 }; 23901 23902 /** 23903 * The player's language code 23904 * NOTE: The language should be set in the player options if you want the 23905 * the controls to be built with a specific language. Changing the lanugage 23906 * later will not update controls text. 23907 * 23908 * @param {string} [code] 23909 * the language code to set the player to 23910 * 23911 * @return {string} 23912 * The current language code when getting 23913 */ 23914 23915 23916 Player.prototype.language = function language(code) { 23917 if (code === undefined) { 23918 return this.language_; 23919 } 23920 23921 this.language_ = String(code).toLowerCase(); 23922 }; 23923 23924 /** 23925 * Get the player's language dictionary 23926 * Merge every time, because a newly added plugin might call videojs.addLanguage() at any time 23927 * Languages specified directly in the player options have precedence 23928 * 23929 * @return {Array} 23930 * An array of of supported languages 23931 */ 23932 23933 23934 Player.prototype.languages = function languages() { 23935 return mergeOptions(Player.prototype.options_.languages, this.languages_); 23936 }; 23937 23938 /** 23939 * returns a JavaScript object reperesenting the current track 23940 * information. **DOES not return it as JSON** 23941 * 23942 * @return {Object} 23943 * Object representing the current of track info 23944 */ 23945 23946 23947 Player.prototype.toJSON = function toJSON() { 23948 var options = mergeOptions(this.options_); 23949 var tracks = options.tracks; 23950 23951 options.tracks = []; 23952 23953 for (var i = 0; i < tracks.length; i++) { 23954 var track = tracks[i]; 23955 23956 // deep merge tracks and null out player so no circular references 23957 track = mergeOptions(track); 23958 track.player = undefined; 23959 options.tracks[i] = track; 23960 } 23961 23962 return options; 23963 }; 23964 23965 /** 23966 * Creates a simple modal dialog (an instance of the {@link ModalDialog} 23967 * component) that immediately overlays the player with arbitrary 23968 * content and removes itself when closed. 23969 * 23970 * @param {string|Function|Element|Array|null} content 23971 * Same as {@link ModalDialog#content}'s param of the same name. 23972 * The most straight-forward usage is to provide a string or DOM 23973 * element. 23974 * 23975 * @param {Object} [options] 23976 * Extra options which will be passed on to the {@link ModalDialog}. 23977 * 23978 * @return {ModalDialog} 23979 * the {@link ModalDialog} that was created 23980 */ 23981 23982 23983 Player.prototype.createModal = function createModal(content, options) { 23984 var _this8 = this; 23985 23986 options = options || {}; 23987 options.content = content || ''; 23988 23989 var modal = new ModalDialog(this, options); 23990 23991 this.addChild(modal); 23992 modal.on('dispose', function () { 23993 _this8.removeChild(modal); 23994 }); 23995 23996 modal.open(); 23997 return modal; 23998 }; 23999 24000 /** 24001 * Gets tag settings 24002 * 24003 * @param {Element} tag 24004 * The player tag 24005 * 24006 * @return {Object}
vendor: 4,271 bytes, lines 24007-24177
24007 * An object containing all of the settings 24008 * for a player tag 24009 */ 24010 24011 24012 Player.getTagSettings = function getTagSettings(tag) { 24013 var baseOptions = { 24014 sources: [], 24015 tracks: [] 24016 }; 24017 24018 var tagOptions = getAttributes(tag); 24019 var dataSetup = tagOptions['data-setup']; 24020 24021 if (hasClass(tag, 'vjs-fluid')) { 24022 tagOptions.fluid = true; 24023 } 24024 24025 // Check if data-setup attr exists. 24026 if (dataSetup !== null) { 24027 // Parse options JSON 24028 // If empty string, make it a parsable json object. 24029 var _safeParseTuple = tuple(dataSetup || '{}'), 24030 err = _safeParseTuple[0], 24031 data = _safeParseTuple[1]; 24032 24033 if (err) { 24034 log$1.error(err); 24035 } 24036 assign(tagOptions, data); 24037 } 24038 24039 assign(baseOptions, tagOptions); 24040 24041 // Get tag children settings 24042 if (tag.hasChildNodes()) { 24043 var children = tag.childNodes; 24044 24045 for (var i = 0, j = children.length; i < j; i++) { 24046 var child = children[i]; 24047 // Change case needed: http://ejohn.org/blog/nodename-case-sensitivity/ 24048 var childName = child.nodeName.toLowerCase(); 24049 24050 if (childName === 'source') { 24051 baseOptions.sources.push(getAttributes(child)); 24052 } else if (childName === 'track') { 24053 baseOptions.tracks.push(getAttributes(child)); 24054 } 24055 } 24056 } 24057 24058 return baseOptions; 24059 }; 24060 24061 /** 24062 * Determine wether or not flexbox is supported 24063 * 24064 * @return {boolean} 24065 * - true if flexbox is supported 24066 * - false if flexbox is not supported 24067 */ 24068 24069 24070 Player.prototype.flexNotSupported_ = function flexNotSupported_() { 24071 var elem = document_1.createElement('i'); 24072 24073 // Note: We don't actually use flexBasis (or flexOrder), but it's one of the more 24074 // common flex features that we can rely on when checking for flex support. 24075 return !('flexBasis' in elem.style || 'webkitFlexBasis' in elem.style || 'mozFlexBasis' in elem.style || 'msFlexBasis' in elem.style || 24076 // IE10-specific (2012 flex spec) 24077 'msFlexOrder' in elem.style); 24078 }; 24079 24080 return Player; 24081}(Component); 24082 24083/** 24084 * Get the {@link VideoTrackList} 24085 * @link https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist 24086 * 24087 * @return {VideoTrackList} 24088 * the current video track list 24089 * 24090 * @method Player.prototype.videoTracks 24091 */ 24092 24093/** 24094 * Get the {@link AudioTrackList} 24095 * @link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist 24096 * 24097 * @return {AudioTrackList} 24098 * the current audio track list 24099 * 24100 * @method Player.prototype.audioTracks 24101 */ 24102 24103/** 24104 * Get the {@link TextTrackList} 24105 * 24106 * @link http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-texttracks 24107 * 24108 * @return {TextTrackList} 24109 * the current text track list 24110 * 24111 * @method Player.prototype.textTracks 24112 */ 24113 24114/** 24115 * Get the remote {@link TextTrackList} 24116 * 24117 * @return {TextTrackList} 24118 * The current remote text track list 24119 * 24120 * @method Player.prototype.remoteTextTracks 24121 */ 24122 24123/** 24124 * Get the remote {@link HtmlTrackElementList} tracks. 24125 * 24126 * @return {HtmlTrackElementList} 24127 * The current remote text track element list 24128 * 24129 * @method Player.prototype.remoteTextTrackEls 24130 */ 24131 24132ALL.names.forEach(function (name$$1) { 24133 var props = ALL[name$$1]; 24134 24135 Player.prototype[props.getterName] = function () { 24136 if (this.tech_) { 24137 return this.tech_[props.getterName](); 24138 } 24139 24140 // if we have not yet loadTech_, we create {video,audio,text}Tracks_ 24141 // these will be passed to the tech during loading 24142 this[props.privateName] = this[props.privateName] || new props.ListClass(); 24143 return this[props.privateName]; 24144 }; 24145}); 24146 24147/** 24148 * Global player list 24149 * 24150 * @type {Object} 24151 */ 24152Player.players = {}; 24153 24154var navigator$1 = window_1.navigator; 24155 24156/* 24157 * Player instance options, surfaced using options 24158 * options = Player.prototype.options_ 24159 * Make changes in options, not here. 24160 * 24161 * @type {Object} 24162 * @private 24163 */ 24164Player.prototype.options_ = { 24165 // Default order of fallback technology 24166 techOrder: Tech.defaultTechOrder_, 24167 24168 html5: {}, 24169 flash: {}, 24170 24171 // default inactivity timeout 24172 inactivityTimeout: 2000, 24173 24174 // default playback rates 24175 playbackRates: [], 24176 // Add playback rate selection by adding rates 24177 // 'playbackRates': [0.5, 1, 1.5, 2],
vendor: 4,795 bytes, lines 24178-24332
24178 24179 // Included control sets 24180 children: ['mediaLoader', 'posterImage', 'textTrackDisplay', 'loadingSpinner', 'bigPlayButton', 'controlBar', 'errorDisplay', 'textTrackSettings'], 24181 24182 language: navigator$1 && (navigator$1.languages && navigator$1.languages[0] || navigator$1.userLanguage || navigator$1.language) || 'en', 24183 24184 // locales and their language translations 24185 languages: {}, 24186 24187 // Default message to show when a video cannot be played. 24188 notSupportedMessage: 'No compatible source was found for this media.' 24189}; 24190 24191[ 24192/** 24193 * Returns whether or not the player is in the "ended" state. 24194 * 24195 * @return {Boolean} True if the player is in the ended state, false if not. 24196 * @method Player#ended 24197 */ 24198'ended', 24199/** 24200 * Returns whether or not the player is in the "seeking" state. 24201 * 24202 * @return {Boolean} True if the player is in the seeking state, false if not. 24203 * @method Player#seeking 24204 */ 24205'seeking', 24206/** 24207 * Returns the TimeRanges of the media that are currently available 24208 * for seeking to. 24209 * 24210 * @return {TimeRanges} the seekable intervals of the media timeline 24211 * @method Player#seekable 24212 */ 24213'seekable', 24214/** 24215 * Returns the current state of network activity for the element, from 24216 * the codes in the list below. 24217 * - NETWORK_EMPTY (numeric value 0) 24218 * The element has not yet been initialised. All attributes are in 24219 * their initial states. 24220 * - NETWORK_IDLE (numeric value 1) 24221 * The element's resource selection algorithm is active and has 24222 * selected a resource, but it is not actually using the network at 24223 * this time. 24224 * - NETWORK_LOADING (numeric value 2) 24225 * The user agent is actively trying to download data. 24226 * - NETWORK_NO_SOURCE (numeric value 3) 24227 * The element's resource selection algorithm is active, but it has 24228 * not yet found a resource to use. 24229 * 24230 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#network-states 24231 * @return {number} the current network activity state 24232 * @method Player#networkState 24233 */ 24234'networkState', 24235/** 24236 * Returns a value that expresses the current state of the element 24237 * with respect to rendering the current playback position, from the 24238 * codes in the list below. 24239 * - HAVE_NOTHING (numeric value 0) 24240 * No information regarding the media resource is available. 24241 * - HAVE_METADATA (numeric value 1) 24242 * Enough of the resource has been obtained that the duration of the 24243 * resource is available. 24244 * - HAVE_CURRENT_DATA (numeric value 2) 24245 * Data for the immediate current playback position is available. 24246 * - HAVE_FUTURE_DATA (numeric value 3) 24247 * Data for the immediate current playback position is available, as 24248 * well as enough data for the user agent to advance the current 24249 * playback position in the direction of playback. 24250 * - HAVE_ENOUGH_DATA (numeric value 4) 24251 * The user agent estimates that enough data is available for 24252 * playback to proceed uninterrupted. 24253 * 24254 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-readystate 24255 * @return {number} the current playback rendering state 24256 * @method Player#readyState 24257 */ 24258'readyState'].forEach(function (fn) { 24259 Player.prototype[fn] = function () { 24260 return this.techGet_(fn); 24261 }; 24262}); 24263 24264TECH_EVENTS_RETRIGGER.forEach(function (event) { 24265 Player.prototype['handleTech' + toTitleCase(event) + '_'] = function () { 24266 return this.trigger(event); 24267 }; 24268}); 24269 24270/** 24271 * Fired when the player has initial duration and dimension information 24272 * 24273 * @event Player#loadedmetadata 24274 * @type {EventTarget~Event} 24275 */ 24276 24277/** 24278 * Fired when the player has downloaded data at the current playback position 24279 * 24280 * @event Player#loadeddata 24281 * @type {EventTarget~Event} 24282 */ 24283 24284/** 24285 * Fired when the current playback position has changed * 24286 * During playback this is fired every 15-250 milliseconds, depending on the 24287 * playback technology in use. 24288 * 24289 * @event Player#timeupdate 24290 * @type {EventTarget~Event} 24291 */ 24292 24293/** 24294 * Fired when the volume changes 24295 * 24296 * @event Player#volumechange 24297 * @type {EventTarget~Event} 24298 */ 24299 24300/** 24301 * Reports whether or not a player has a plugin available. 24302 * 24303 * This does not report whether or not the plugin has ever been initialized 24304 * on this player. For that, [usingPlugin]{@link Player#usingPlugin}. 24305 * 24306 * @method Player#hasPlugin 24307 * @param {string} name 24308 * The name of a plugin. 24309 * 24310 * @return {boolean} 24311 * Whether or not this player has the requested plugin available. 24312 */ 24313 24314/** 24315 * Reports whether or not a player is using a plugin by name. 24316 * 24317 * For basic plugins, this only reports whether the plugin has _ever_ been 24318 * initialized on this player. 24319 * 24320 * @method Player#usingPlugin 24321 * @param {string} name 24322 * The name of a plugin. 24323 * 24324 * @return {boolean} 24325 * Whether or not this player is using the requested plugin. 24326 */ 24327 24328Component.registerComponent('Player', Player); 24329 24330/** 24331 * @file plugin.js 24332 */
vendor: 13,630 bytes, lines 24332-24840
24332 24333/** 24334 * The base plugin name. 24335 * 24336 * @private 24337 * @constant 24338 * @type {string} 24339 */ 24340var BASE_PLUGIN_NAME = 'plugin'; 24341 24342/** 24343 * The key on which a player's active plugins cache is stored. 24344 * 24345 * @private 24346 * @constant 24347 * @type {string} 24348 */ 24349var PLUGIN_CACHE_KEY = 'activePlugins_'; 24350 24351/** 24352 * Stores registered plugins in a private space. 24353 * 24354 * @private 24355 * @type {Object} 24356 */ 24357var pluginStorage = {}; 24358 24359/** 24360 * Reports whether or not a plugin has been registered. 24361 * 24362 * @private 24363 * @param {string} name 24364 * The name of a plugin. 24365 * 24366 * @returns {boolean} 24367 * Whether or not the plugin has been registered. 24368 */ 24369var pluginExists = function pluginExists(name) { 24370 return pluginStorage.hasOwnProperty(name); 24371}; 24372 24373/** 24374 * Get a single registered plugin by name. 24375 * 24376 * @private 24377 * @param {string} name 24378 * The name of a plugin. 24379 * 24380 * @returns {Function|undefined} 24381 * The plugin (or undefined). 24382 */ 24383var getPlugin = function getPlugin(name) { 24384 return pluginExists(name) ? pluginStorage[name] : undefined; 24385}; 24386 24387/** 24388 * Marks a plugin as "active" on a player. 24389 * 24390 * Also, ensures that the player has an object for tracking active plugins. 24391 * 24392 * @private 24393 * @param {Player} player 24394 * A Video.js player instance. 24395 * 24396 * @param {string} name 24397 * The name of a plugin. 24398 */ 24399var markPluginAsActive = function markPluginAsActive(player, name) { 24400 player[PLUGIN_CACHE_KEY] = player[PLUGIN_CACHE_KEY] || {}; 24401 player[PLUGIN_CACHE_KEY][name] = true; 24402}; 24403 24404/** 24405 * Triggers a pair of plugin setup events. 24406 * 24407 * @private 24408 * @param {Player} player 24409 * A Video.js player instance. 24410 * 24411 * @param {Plugin~PluginEventHash} hash 24412 * A plugin event hash. 24413 * 24414 * @param {Boolean} [before] 24415 * If true, prefixes the event name with "before". In other words, 24416 * use this to trigger "beforepluginsetup" instead of "pluginsetup". 24417 */ 24418var triggerSetupEvent = function triggerSetupEvent(player, hash, before) { 24419 var eventName = (before ? 'before' : '') + 'pluginsetup'; 24420 24421 player.trigger(eventName, hash); 24422 player.trigger(eventName + ':' + hash.name, hash); 24423}; 24424 24425/** 24426 * Takes a basic plugin function and returns a wrapper function which marks 24427 * on the player that the plugin has been activated. 24428 * 24429 * @private 24430 * @param {string} name 24431 * The name of the plugin. 24432 * 24433 * @param {Function} plugin 24434 * The basic plugin. 24435 * 24436 * @returns {Function} 24437 * A wrapper function for the given plugin. 24438 */ 24439var createBasicPlugin = function createBasicPlugin(name, plugin) { 24440 var basicPluginWrapper = function basicPluginWrapper() { 24441 24442 // We trigger the "beforepluginsetup" and "pluginsetup" events on the player 24443 // regardless, but we want the hash to be consistent with the hash provided 24444 // for advanced plugins. 24445 // 24446 // The only potentially counter-intuitive thing here is the `instance` in 24447 // the "pluginsetup" event is the value returned by the `plugin` function. 24448 triggerSetupEvent(this, { name: name, plugin: plugin, instance: null }, true); 24449 24450 var instance = plugin.apply(this, arguments); 24451 24452 markPluginAsActive(this, name); 24453 triggerSetupEvent(this, { name: name, plugin: plugin, instance: instance }); 24454 24455 return instance; 24456 }; 24457 24458 Object.keys(plugin).forEach(function (prop) { 24459 basicPluginWrapper[prop] = plugin[prop]; 24460 }); 24461 24462 return basicPluginWrapper; 24463}; 24464 24465/** 24466 * Takes a plugin sub-class and returns a factory function for generating 24467 * instances of it. 24468 * 24469 * This factory function will replace itself with an instance of the requested 24470 * sub-class of Plugin. 24471 * 24472 * @private 24473 * @param {string} name 24474 * The name of the plugin. 24475 * 24476 * @param {Plugin} PluginSubClass 24477 * The advanced plugin. 24478 * 24479 * @returns {Function} 24480 */ 24481var createPluginFactory = function createPluginFactory(name, PluginSubClass) { 24482 24483 // Add a `name` property to the plugin prototype so that each plugin can 24484 // refer to itself by name. 24485 PluginSubClass.prototype.name = name; 24486 24487 return function () { 24488 triggerSetupEvent(this, { name: name, plugin: PluginSubClass, instance: null }, true); 24489 24490 for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { 24491 args[_key] = arguments[_key]; 24492 } 24493 24494 var instance = new (Function.prototype.bind.apply(PluginSubClass, [null].concat([this].concat(args))))(); 24495 24496 // The plugin is replaced by a function that returns the current instance. 24497 this[name] = function () { 24498 return instance; 24499 }; 24500 24501 triggerSetupEvent(this, instance.getEventHash()); 24502 24503 return instance; 24504 }; 24505}; 24506 24507/** 24508 * Parent class for all advanced plugins. 24509 * 24510 * @mixes module:evented~EventedMixin 24511 * @mixes module:stateful~StatefulMixin 24512 * @fires Player#beforepluginsetup 24513 * @fires Player#beforepluginsetup:$name 24514 * @fires Player#pluginsetup 24515 * @fires Player#pluginsetup:$name 24516 * @listens Player#dispose 24517 * @throws {Error} 24518 * If attempting to instantiate the base {@link Plugin} class 24519 * directly instead of via a sub-class. 24520 */ 24521 24522var Plugin = function () { 24523 24524 /** 24525 * Creates an instance of this class. 24526 * 24527 * Sub-classes should call `super` to ensure plugins are properly initialized. 24528 * 24529 * @param {Player} player 24530 * A Video.js player instance. 24531 */ 24532 function Plugin(player) { 24533 classCallCheck(this, Plugin); 24534 24535 if (this.constructor === Plugin) { 24536 throw new Error('Plugin must be sub-classed; not directly instantiated.'); 24537 } 24538 24539 this.player = player; 24540 24541 // Make this object evented, but remove the added `trigger` method so we 24542 // use the prototype version instead. 24543 evented(this); 24544 delete this.trigger; 24545 24546 stateful(this, this.constructor.defaultState); 24547 markPluginAsActive(player, this.name); 24548 24549 // Auto-bind the dispose method so we can use it as a listener and unbind 24550 // it later easily. 24551 this.dispose = bind(this, this.dispose); 24552 24553 // If the player is disposed, dispose the plugin. 24554 player.on('dispose', this.dispose); 24555 } 24556 24557 /** 24558 * Get the version of the plugin that was set on <pluginName>.VERSION 24559 */ 24560 24561 24562 Plugin.prototype.version = function version() { 24563 return this.constructor.VERSION; 24564 }; 24565 24566 /** 24567 * Each event triggered by plugins includes a hash of additional data with 24568 * conventional properties. 24569 * 24570 * This returns that object or mutates an existing hash. 24571 * 24572 * @param {Object} [hash={}] 24573 * An object to be used as event an event hash. 24574 * 24575 * @returns {Plugin~PluginEventHash} 24576 * An event hash object with provided properties mixed-in. 24577 */ 24578 24579 24580 Plugin.prototype.getEventHash = function getEventHash() { 24581 var hash = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 24582 24583 hash.name = this.name; 24584 hash.plugin = this.constructor; 24585 hash.instance = this; 24586 return hash; 24587 }; 24588 24589 /** 24590 * Triggers an event on the plugin object and overrides 24591 * {@link module:evented~EventedMixin.trigger|EventedMixin.trigger}. 24592 * 24593 * @param {string|Object} event 24594 * An event type or an object with a type property. 24595 * 24596 * @param {Object} [hash={}] 24597 * Additional data hash to merge with a 24598 * {@link Plugin~PluginEventHash|PluginEventHash}. 24599 * 24600 * @returns {boolean} 24601 * Whether or not default was prevented. 24602 */ 24603 24604 24605 Plugin.prototype.trigger = function trigger$$1(event) { 24606 var hash = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 24607 24608 return trigger(this.eventBusEl_, event, this.getEventHash(hash)); 24609 }; 24610 24611 /** 24612 * Handles "statechanged" events on the plugin. No-op by default, override by 24613 * subclassing. 24614 * 24615 * @abstract 24616 * @param {Event} e 24617 * An event object provided by a "statechanged" event. 24618 * 24619 * @param {Object} e.changes 24620 * An object describing changes that occurred with the "statechanged" 24621 * event. 24622 */ 24623 24624 24625 Plugin.prototype.handleStateChanged = function handleStateChanged(e) {}; 24626 24627 /** 24628 * Disposes a plugin. 24629 * 24630 * Subclasses can override this if they want, but for the sake of safety, 24631 * it's probably best to subscribe the "dispose" event. 24632 * 24633 * @fires Plugin#dispose 24634 */ 24635 24636 24637 Plugin.prototype.dispose = function dispose() { 24638 var name = this.name, 24639 player = this.player; 24640 24641 /** 24642 * Signals that a advanced plugin is about to be disposed. 24643 * 24644 * @event Plugin#dispose 24645 * @type {EventTarget~Event} 24646 */ 24647 24648 this.trigger('dispose'); 24649 this.off(); 24650 player.off('dispose', this.dispose); 24651 24652 // Eliminate any possible sources of leaking memory by clearing up 24653 // references between the player and the plugin instance and nulling out 24654 // the plugin's state and replacing methods with a function that throws. 24655 player[PLUGIN_CACHE_KEY][name] = false; 24656 this.player = this.state = null; 24657 24658 // Finally, replace the plugin name on the player with a new factory 24659 // function, so that the plugin is ready to be set up again. 24660 player[name] = createPluginFactory(name, pluginStorage[name]); 24661 }; 24662 24663 /** 24664 * Determines if a plugin is a basic plugin (i.e. not a sub-class of `Plugin`). 24665 * 24666 * @param {string|Function} plugin 24667 * If a string, matches the name of a plugin. If a function, will be 24668 * tested directly. 24669 * 24670 * @returns {boolean} 24671 * Whether or not a plugin is a basic plugin. 24672 */ 24673 24674 24675 Plugin.isBasic = function isBasic(plugin) { 24676 var p = typeof plugin === 'string' ? getPlugin(plugin) : plugin; 24677 24678 return typeof p === 'function' && !Plugin.prototype.isPrototypeOf(p.prototype); 24679 }; 24680 24681 /** 24682 * Register a Video.js plugin. 24683 * 24684 * @param {string} name 24685 * The name of the plugin to be registered. Must be a string and 24686 * must not match an existing plugin or a method on the `Player` 24687 * prototype. 24688 * 24689 * @param {Function} plugin 24690 * A sub-class of `Plugin` or a function for basic plugins. 24691 * 24692 * @returns {Function} 24693 * For advanced plugins, a factory function for that plugin. For 24694 * basic plugins, a wrapper function that initializes the plugin. 24695 */ 24696 24697 24698 Plugin.registerPlugin = function registerPlugin(name, plugin) { 24699 if (typeof name !== 'string') { 24700 throw new Error('Illegal plugin name, "' + name + '", must be a string, was ' + (typeof name === 'undefined' ? 'undefined' : _typeof(name)) + '.'); 24701 } 24702 24703 if (pluginExists(name)) { 24704 log$1.warn('A plugin named "' + name + '" already exists. You may want to avoid re-registering plugins!'); 24705 } else if (Player.prototype.hasOwnProperty(name)) { 24706 throw new Error('Illegal plugin name, "' + name + '", cannot share a name with an existing player method!'); 24707 } 24708 24709 if (typeof plugin !== 'function') { 24710 throw new Error('Illegal plugin for "' + name + '", must be a function, was ' + (typeof plugin === 'undefined' ? 'undefined' : _typeof(plugin)) + '.'); 24711 } 24712 24713 pluginStorage[name] = plugin; 24714 24715 // Add a player prototype method for all sub-classed plugins (but not for 24716 // the base Plugin class). 24717 if (name !== BASE_PLUGIN_NAME) { 24718 if (Plugin.isBasic(plugin)) { 24719 Player.prototype[name] = createBasicPlugin(name, plugin); 24720 } else { 24721 Player.prototype[name] = createPluginFactory(name, plugin); 24722 } 24723 } 24724 24725 return plugin; 24726 }; 24727 24728 /** 24729 * De-register a Video.js plugin. 24730 * 24731 * @param {string} name 24732 * The name of the plugin to be deregistered. 24733 */ 24734 24735 24736 Plugin.deregisterPlugin = function deregisterPlugin(name) { 24737 if (name === BASE_PLUGIN_NAME) { 24738 throw new Error('Cannot de-register base plugin.'); 24739 } 24740 if (pluginExists(name)) { 24741 delete pluginStorage[name]; 24742 delete Player.prototype[name]; 24743 } 24744 }; 24745 24746 /** 24747 * Gets an object containing multiple Video.js plugins. 24748 * 24749 * @param {Array} [names] 24750 * If provided, should be an array of plugin names. Defaults to _all_ 24751 * plugin names. 24752 * 24753 * @returns {Object|undefined} 24754 * An object containing plugin(s) associated with their name(s) or 24755 * `undefined` if no matching plugins exist). 24756 */ 24757 24758 24759 Plugin.getPlugins = function getPlugins() { 24760 var names = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : Object.keys(pluginStorage); 24761 24762 var result = void 0; 24763 24764 names.forEach(function (name) { 24765 var plugin = getPlugin(name); 24766 24767 if (plugin) { 24768 result = result || {}; 24769 result[name] = plugin; 24770 } 24771 }); 24772 24773 return result; 24774 }; 24775 24776 /** 24777 * Gets a plugin's version, if available 24778 * 24779 * @param {string} name 24780 * The name of a plugin. 24781 * 24782 * @returns {string} 24783 * The plugin's version or an empty string. 24784 */ 24785 24786 24787 Plugin.getPluginVersion = function getPluginVersion(name) { 24788 var plugin = getPlugin(name); 24789 24790 return plugin && plugin.VERSION || ''; 24791 }; 24792 24793 return Plugin; 24794}(); 24795 24796/** 24797 * Gets a plugin by name if it exists. 24798 * 24799 * @static 24800 * @method getPlugin 24801 * @memberOf Plugin 24802 * @param {string} name 24803 * The name of a plugin. 24804 * 24805 * @returns {Function|undefined} 24806 * The plugin (or `undefined`). 24807 */ 24808 24809 24810Plugin.getPlugin = getPlugin; 24811 24812/** 24813 * The name of the base plugin class as it is registered. 24814 * 24815 * @type {string} 24816 */ 24817Plugin.BASE_PLUGIN_NAME = BASE_PLUGIN_NAME; 24818 24819Plugin.registerPlugin(BASE_PLUGIN_NAME, Plugin); 24820 24821/** 24822 * Documented in player.js 24823 * 24824 * @ignore 24825 */ 24826Player.prototype.usingPlugin = function (name) { 24827 return !!this[PLUGIN_CACHE_KEY] && this[PLUGIN_CACHE_KEY][name] === true; 24828}; 24829 24830/** 24831 * Documented in player.js 24832 * 24833 * @ignore 24834 */ 24835Player.prototype.hasPlugin = function (name) { 24836 return !!pluginExists(name); 24837}; 24838 24839/** 24840 *
vendor: 3,442 bytes, lines 24840-24973
24840Signals that a plugin is about to be set up on a player. 24841 * 24842 * @event Player#beforepluginsetup 24843 * @type {Plugin~PluginEventHash} 24844 */ 24845 24846/** 24847 * Signals that a plugin is about to be set up on a player - by name. The name 24848 * is the name of the plugin. 24849 * 24850 * @event Player#beforepluginsetup:$name 24851 * @type {Plugin~PluginEventHash} 24852 */ 24853 24854/** 24855 * Signals that a plugin has just been set up on a player. 24856 * 24857 * @event Player#pluginsetup 24858 * @type {Plugin~PluginEventHash} 24859 */ 24860 24861/** 24862 * Signals that a plugin has just been set up on a player - by name. The name 24863 * is the name of the plugin. 24864 * 24865 * @event Player#pluginsetup:$name 24866 * @type {Plugin~PluginEventHash} 24867 */ 24868 24869/** 24870 * @typedef {Object} Plugin~PluginEventHash 24871 * 24872 * @property {string} instance 24873 * For basic plugins, the return value of the plugin function. For 24874 * advanced plugins, the plugin instance on which the event is fired. 24875 * 24876 * @property {string} name 24877 * The name of the plugin. 24878 * 24879 * @property {string} plugin 24880 * For basic plugins, the plugin function. For advanced plugins, the 24881 * plugin class/constructor. 24882 */ 24883 24884/** 24885 * @file extend.js 24886 * @module extend 24887 */ 24888 24889/** 24890 * A combination of node inherits and babel's inherits (after transpile). 24891 * Both work the same but node adds `super_` to the subClass 24892 * and Bable adds the superClass as __proto__. Both seem useful. 24893 * 24894 * @param {Object} subClass 24895 * The class to inherit to 24896 * 24897 * @param {Object} superClass 24898 * The class to inherit from 24899 * 24900 * @private 24901 */ 24902var _inherits = function _inherits(subClass, superClass) { 24903 if (typeof superClass !== 'function' && superClass !== null) { 24904 throw new TypeError('Super expression must either be null or a function, not ' + (typeof superClass === 'undefined' ? 'undefined' : _typeof(superClass))); 24905 } 24906 24907 subClass.prototype = Object.create(superClass && superClass.prototype, { 24908 constructor: { 24909 value: subClass, 24910 enumerable: false, 24911 writable: true, 24912 configurable: true 24913 } 24914 }); 24915 24916 if (superClass) { 24917 // node 24918 subClass.super_ = superClass; 24919 } 24920}; 24921 24922/** 24923 * Function for subclassing using the same inheritance that 24924 * videojs uses internally 24925 * 24926 * @static 24927 * @const 24928 * 24929 * @param {Object} superClass 24930 * The class to inherit from 24931 * 24932 * @param {Object} [subClassMethods={}] 24933 * The class to inherit to 24934 * 24935 * @return {Object} 24936 * The new object with subClassMethods that inherited superClass. 24937 */ 24938var extendFn = function extendFn(superClass) { 24939 var subClassMethods = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 24940 24941 var subClass = function subClass() { 24942 superClass.apply(this, arguments); 24943 }; 24944 24945 var methods = {}; 24946 24947 if ((typeof subClassMethods === 'undefined' ? 'undefined' : _typeof(subClassMethods)) === 'object') { 24948 if (subClassMethods.constructor !== Object.prototype.constructor) { 24949 subClass = subClassMethods.constructor; 24950 } 24951 methods = subClassMethods; 24952 } else if (typeof subClassMethods === 'function') { 24953 subClass = subClassMethods; 24954 } 24955 24956 _inherits(subClass, superClass); 24957 24958 // Extend subObj's prototype with functions and other properties from props 24959 for (var name in methods) { 24960 if (methods.hasOwnProperty(name)) { 24961 subClass.prototype[name] = methods[name]; 24962 } 24963 } 24964 24965 return subClass; 24966}; 24967 24968/** 24969 * @file video.js 24970 * @module videojs 24971 */ 24972// Include the built-in techs 24973// HTML5 Element Shim for IE8
24974if (typeof HTMLVideoElement === 'undefined' && isReal()) { 24975 document_1.createElement('video'); 24976 document_1.createElement('audio'); 24977 document_1.createElement('track'); 24978 document_1.createElement('video-js'); 24979} 24980 24981/** 24982 * Doubles as the main function for users to create a player instance and also 24983 * the main library object. 24984 * The `videojs` function can be used to initialize or retrieve a player. 24985 * 24986 * @param {string|Element} id 24987 * Video element or video element ID 24988 * 24989 * @param {Object} [options] 24990 * Optional options object for config/settings 24991 * 24992 * @param {Component~ReadyCallback} [ready] 24993 * Optional ready callback 24994 * 24995 * @return {Player} 24996 * A player instance 24997 */ 24998function videojs(id, options, ready) { 24999 var tag = void 0; 25000 25001 // Allow for element or ID to be passed in 25002 // String ID 25003 if (typeof id === 'string') { 25004 var players = videojs.getPlayers(); 25005 25006 // Adjust for jQuery ID syntax 25007 if (id.indexOf('#') === 0) { 25008 id = id.slice(1); 25009 } 25010 25011 // If a player instance has already been created for this ID return it. 25012 if (players[id]) { 25013 25014 // If options or ready function are passed, warn 25015 if (options) { 25016 log$1.warn('Player "' + id + '" is already initialised. Options will not be applied.'); 25017 } 25018 25019 if (ready) { 25020 players[id].ready(ready); 25021 } 25022 25023 return players[id]; 25024 } 25025 25026 // Otherwise get element for ID 25027 tag = $('#' + id); 25028 25029 // ID is a media element 25030 } else { 25031 tag = id; 25032 } 25033 25034 // Check for a useable element 25035 // re: nodeName, could be a box div also 25036 if (!tag || !tag.nodeName) { 25037 throw new TypeError('The element or ID supplied is not valid. (videojs)'); 25038 } 25039 25040 // Element may have a player attr referring to an already created player instance. 25041 // If so return that otherwise set up a new player below 25042 if (tag.player || Player.players[tag.playerId]) { 25043 return tag.player || Player.players[tag.playerId]; 25044 } 25045 25046 // Check if element is included in the DOM 25047 if (isEl(tag) && !document_1.body.contains(tag)) { 25048 log$1.warn('The element supplied is not included in the DOM'); 25049 } 25050 25051 options = options || {}; 25052 25053 videojs.hooks('beforesetup').forEach(function (hookFunction) { 25054 var opts = hookFunction(tag, mergeOptions(options)); 25055 25056 if (!isObject(opts) || Array.isArray(opts)) { 25057 log$1.error('please return an object in beforesetup hooks'); 25058 return; 25059 } 25060 25061 options = mergeOptions(options, opts); 25062 }); 25063 25064 var PlayerComponent = Component.getComponent('Player'); 25065 // If not, set up a new player 25066 var player = new PlayerComponent(tag, options, ready); 25067 25068 videojs.hooks('setup').forEach(function (hookFunction) { 25069 return hookFunction(player); 25070 }); 25071 25072 return player; 25073} 25074 25075/** 25076 * An Object that contains lifecycle hooks as keys which point to an array 25077 * of functions that are run when a lifecycle is triggered 25078 */ 25079videojs.hooks_ = {}; 25080 25081/** 25082 * Get a list of hooks for a specific lifecycle 25083 * @function videojs.hooks 25084 * 25085 * @param {string} type 25086 * the lifecyle to get hooks from 25087 * 25088 * @param {Function|Function[]} [fn] 25089 * Optionally add a hook (or hooks) to the lifecycle that your are getting. 25090 * 25091 * @return {Array} 25092 * an array of hooks, or an empty array if there are none. 25093 */ 25094videojs.hooks = function (type, fn) { 25095 videojs.hooks_[type] = videojs.hooks_[type] || []; 25096 if (fn) { 25097 videojs.hooks_[type] = videojs.hooks_[type].concat(fn); 25098 } 25099 return videojs.hooks_[type]; 25100}; 25101 25102/** 25103 * Add a function hook to a specific videojs lifecycle. 25104 * 25105 * @param {string} type 25106 * the lifecycle to hook the function to. 25107 * 25108 * @param {Function|Function[]} 25109 * The function or array of functions to attach. 25110 */ 25111videojs.hook = function (type, fn) { 25112 videojs.hooks(type, fn); 25113}; 25114 25115/** 25116 * Add a function hook that will only run once to a specific videojs lifecycle. 25117 * 25118 * @param {string} type 25119 * the lifecycle to hook the function to. 25120 * 25121 * @param {Function|Function[]} 25122 * The function or array of functions to attach. 25123 */ 25124videojs.hookOnce = function (type, fn) { 25125 videojs.hooks(type, [].concat(fn).map(function (original) { 25126 var wrapper = function wrapper() { 25127 videojs.removeHook(type, wrapper); 25128 return original.apply(undefined, arguments); 25129 }; 25130 25131 return wrapper; 25132 })); 25133}; 25134 25135/** 25136 * Remove a hook from a specific videojs lifecycle. 25137 * 25138 * @param {string} type 25139 * the lifecycle that the function hooked to 25140 * 25141 * @param {Function} fn 25142 * The hooked function to remove 25143 * 25144 * @return {boolean} 25145 * The function that was removed or undef 25146 */
vendor: 5,198 bytes, lines 25147-25345
25147videojs.removeHook = function (type, fn) { 25148 var index = videojs.hooks(type).indexOf(fn); 25149 25150 if (index <= -1) { 25151 return false; 25152 } 25153 25154 videojs.hooks_[type] = videojs.hooks_[type].slice(); 25155 videojs.hooks_[type].splice(index, 1); 25156 25157 return true; 25158}; 25159 25160// Add default styles 25161if (window_1.VIDEOJS_NO_DYNAMIC_STYLE !== true && isReal()) { 25162 var style = $('.vjs-styles-defaults'); 25163 25164 if (!style) { 25165 style = createStyleElement('vjs-styles-defaults'); 25166 var head = $('head'); 25167 25168 if (head) { 25169 head.insertBefore(style, head.firstChild); 25170 } 25171 setTextContent(style, '\n .video-js {\n width: 300px;\n height: 150px;\n }\n\n .vjs-fluid {\n padding-top: 56.25%\n }\n '); 25172 } 25173} 25174 25175// Run Auto-load players 25176// You have to wait at least once in case this script is loaded after your 25177// video in the DOM (weird behavior only with minified version) 25178autoSetupTimeout(1, videojs); 25179 25180/** 25181 * Current software version. Follows semver. 25182 * 25183 * @type {string} 25184 */ 25185videojs.VERSION = version; 25186 25187/** 25188 * The global options object. These are the settings that take effect 25189 * if no overrides are specified when the player is created. 25190 * 25191 * @type {Object} 25192 */ 25193videojs.options = Player.prototype.options_; 25194 25195/** 25196 * Get an object with the currently created players, keyed by player ID 25197 * 25198 * @return {Object} 25199 * The created players 25200 */ 25201videojs.getPlayers = function () { 25202 return Player.players; 25203}; 25204 25205/** 25206 * Expose players object. 25207 * 25208 * @memberOf videojs 25209 * @property {Object} players 25210 */ 25211videojs.players = Player.players; 25212 25213/** 25214 * Get a component class object by name 25215 * 25216 * @borrows Component.getComponent as videojs.getComponent 25217 */ 25218videojs.getComponent = Component.getComponent; 25219 25220/** 25221 * Register a component so it can referred to by name. Used when adding to other 25222 * components, either through addChild `component.addChild('myComponent')` or through 25223 * default children options `{ children: ['myComponent'] }`. 25224 * 25225 * > NOTE: You could also just initialize the component before adding. 25226 * `component.addChild(new MyComponent());` 25227 * 25228 * @param {string} name 25229 * The class name of the component 25230 * 25231 * @param {Component} comp 25232 * The component class 25233 * 25234 * @return {Component} 25235 * The newly registered component 25236 */ 25237videojs.registerComponent = function (name$$1, comp) { 25238 if (Tech.isTech(comp)) { 25239 log$1.warn('The ' + name$$1 + ' tech was registered as a component. It should instead be registered using videojs.registerTech(name, tech)'); 25240 } 25241 25242 Component.registerComponent.call(Component, name$$1, comp); 25243}; 25244 25245/** 25246 * Get a Tech class object by name 25247 * 25248 * @borrows Tech.getTech as videojs.getTech 25249 */ 25250videojs.getTech = Tech.getTech; 25251 25252/** 25253 * Register a Tech so it can referred to by name. 25254 * This is used in the tech order for the player. 25255 * 25256 * @borrows Tech.registerTech as videojs.registerTech 25257 */ 25258videojs.registerTech = Tech.registerTech; 25259 25260videojs.use = use; 25261 25262/** 25263 * A suite of browser and device tests from {@link browser}. 25264 * 25265 * @type {Object} 25266 * @private 25267 */ 25268videojs.browser = browser; 25269 25270/** 25271 * Whether or not the browser supports touch events. Included for backward 25272 * compatibility with 4.x, but deprecated. Use `videojs.browser.TOUCH_ENABLED` 25273 * instead going forward. 25274 * 25275 * @deprecated since version 5.0 25276 * @type {boolean} 25277 */ 25278videojs.TOUCH_ENABLED = TOUCH_ENABLED; 25279 25280/** 25281 * Subclass an existing class 25282 * Mimics ES6 subclassing with the `extend` keyword 25283 * 25284 * @borrows extend:extendFn as videojs.extend 25285 */ 25286videojs.extend = extendFn; 25287 25288/** 25289 * Merge two options objects recursively 25290 * Performs a deep merge like lodash.merge but **only merges plain objects** 25291 * (not arrays, elements, anything else) 25292 * Other values will be copied directly from the second object. 25293 * 25294 * @borrows merge-options:mergeOptions as videojs.mergeOptions 25295 */ 25296videojs.mergeOptions = mergeOptions; 25297 25298/** 25299 * Change the context (this) of a function 25300 * 25301 * > NOTE: as of v5.0 we require an ES5 shim, so you should use the native 25302 * `function() {}.bind(newContext);` instead of this. 25303 * 25304 * @borrows fn:bind as videojs.bind 25305 */ 25306videojs.bind = bind; 25307 25308/** 25309 * Register a Video.js plugin. 25310 * 25311 * @borrows plugin:registerPlugin as videojs.registerPlugin 25312 * @method registerPlugin 25313 * 25314 * @param {string} name 25315 * The name of the plugin to be registered. Must be a string and 25316 * must not match an existing plugin or a method on the `Player` 25317 * prototype. 25318 * 25319 * @param {Function} plugin 25320 * A sub-class of `Plugin` or a function for basic plugins. 25321 * 25322 * @return {Function} 25323 * For advanced plugins, a factory function for that plugin. For 25324 * basic plugins, a wrapper function that initializes the plugin. 25325 */ 25326videojs.registerPlugin = Plugin.registerPlugin; 25327 25328/** 25329 * Deprecated method to register a plugin with Video.js 25330 * 25331 * @deprecated 25332 * videojs.plugin() is deprecated; use videojs.registerPlugin() instead 25333 * 25334 * @param {string} name 25335 * The plugin name 25336 * 25337 * @param {Plugin|Function} plugin 25338 * The plugin sub-class or function 25339 */ 25340videojs.plugin = function (name$$1, plugin) { 25341 log$1.warn('videojs.plugin() is deprecated; use videojs.registerPlugin() instead'); 25342 return Plugin.registerPlugin(name$$1, plugin); 25343}; 25344 25345/**
25346 * Gets an object containing multiple Video.js plugins. 25347 * 25348 * @param {Array} [names] 25349 * If provided, should be an array of plugin names. Defaults to _all_ 25350 * plugin names. 25351 * 25352 * @return {Object|undefined} 25353 * An object containing plugin(s) associated with their name(s) or 25354 * `undefined` if no matching plugins exist). 25355 */ 25356videojs.getPlugins = Plugin.getPlugins; 25357 25358/** 25359 * Gets a plugin by name if it exists. 25360 * 25361 * @param {string} name 25362 * The name of a plugin. 25363 * 25364 * @return {Function|undefined} 25365 * The plugin (or `undefined`). 25366 */ 25367videojs.getPlugin = Plugin.getPlugin; 25368 25369/** 25370 * Gets a plugin's version, if available 25371 * 25372 * @param {string} name 25373 * The name of a plugin. 25374 * 25375 * @return {string} 25376 * The plugin's version or an empty string. 25377 */ 25378videojs.getPluginVersion = Plugin.getPluginVersion; 25379 25380/** 25381 * Adding languages so that they're available to all players. 25382 * Example: `videojs.addLanguage('es', { 'Hello': 'Hola' });` 25383 * 25384 * @param {string} code 25385 * The language code or dictionary property 25386 * 25387 * @param {Object} data 25388 * The data values to be translated 25389 * 25390 * @return {Object} 25391 * The resulting language dictionary object 25392 */ 25393videojs.addLanguage = function (code, data) { 25394 var _mergeOptions; 25395 25396 code = ('' + code).toLowerCase(); 25397 25398 videojs.options.languages = mergeOptions(videojs.options.languages, (_mergeOptions = {}, _mergeOptions[code] = data, _mergeOptions)); 25399 25400 return videojs.options.languages[code]; 25401}; 25402 25403/** 25404 * Log messages 25405 * 25406 * @borrows log:log as videojs.log 25407 */ 25408videojs.log = log$1; 25409 25410/** 25411 * Creates an emulated TimeRange object. 25412 * 25413 * @borrows time-ranges:createTimeRanges as videojs.createTimeRange 25414 */ 25415/** 25416 * @borrows time-ranges:createTimeRanges as videojs.createTimeRanges 25417 */ 25418videojs.createTimeRange = videojs.createTimeRanges = createTimeRanges; 25419 25420/** 25421 * Format seconds as a time string, H:MM:SS or M:SS 25422 * Supplying a guide (in seconds) will force a number of leading zeros 25423 * to cover the length of the guide 25424 * 25425 * @borrows format-time:formatTime as videojs.formatTime 25426 */ 25427videojs.formatTime = formatTime; 25428 25429/** 25430 * Resolve and parse the elements of a URL 25431 * 25432 * @borrows url:parseUrl as videojs.parseUrl 25433 */ 25434videojs.parseUrl = parseUrl; 25435 25436/** 25437 * Returns whether the url passed is a cross domain request or not. 25438 * 25439 * @borrows url:isCrossOrigin as videojs.isCrossOrigin 25440 */ 25441videojs.isCrossOrigin = isCrossOrigin; 25442 25443/** 25444 * Event target class. 25445 * 25446 * @borrows EventTarget as videojs.EventTarget 25447 */ 25448videojs.EventTarget = EventTarget; 25449 25450/** 25451 * Add an event listener to element 25452 * It stores the handler function in a separate cache object 25453 * and adds a generic handler to the element's event, 25454 * along with a unique id (guid) to the element. 25455 * 25456 * @borrows events:on as videojs.on 25457 */ 25458videojs.on = on; 25459 25460/** 25461 * Trigger a listener only once for an event 25462 * 25463 * @borrows events:one as videojs.one 25464 */ 25465videojs.one = one; 25466 25467/** 25468 * Removes event listeners from an element 25469 * 25470 * @borrows events:off as videojs.off 25471 */ 25472videojs.off = off; 25473 25474/** 25475 * Trigger an event for an element 25476 * 25477 * @borrows events:trigger as videojs.trigger 25478 */ 25479videojs.trigger = trigger; 25480 25481/** 25482 * A cross-browser XMLHttpRequest wrapper. Here's a simple example: 25483 * 25484 * @param {Object} options 25485 * settings for the request. 25486 * 25487 * @return {XMLHttpRequest|XDomainRequest} 25488 * The request object. 25489 * 25490 * @see https://github.com/Raynos/xhr 25491 */ 25492videojs.xhr = xhr; 25493 25494/** 25495 * TextTrack class 25496 * 25497 * @borrows TextTrack as videojs.TextTrack 25498 */ 25499videojs.TextTrack = TextTrack; 25500 25501/** 25502 * export the AudioTrack class so that source handlers can create 25503 * AudioTracks and then add them to the players AudioTrackList 25504 * 25505 * @borrows AudioTrack as videojs.AudioTrack 25506 */ 25507videojs.AudioTrack = AudioTrack; 25508 25509/** 25510 * export the VideoTrack class so that source handlers can create 25511 * VideoTracks and then add them to the players VideoTrackList 25512 * 25513 * @borrows VideoTrack as videojs.VideoTrack 25514 */ 25515videojs.VideoTrack = VideoTrack; 25516 25517/** 25518 * Determines, via duck typing, whether or not a value is a DOM element. 25519 * 25520 * @borrows dom:isEl as videojs.isEl 25521 * @deprecated Use videojs.dom.isEl() instead 25522 */ 25523
25524/** 25525 * Determines, via duck typing, whether or not a value is a text node. 25526 * 25527 * @borrows dom:isTextNode as videojs.isTextNode 25528 * @deprecated Use videojs.dom.isTextNode() instead 25529 */ 25530 25531/** 25532 * Creates an element and applies properties. 25533 * 25534 * @borrows dom:createEl as videojs.createEl 25535 * @deprecated Use videojs.dom.createEl() instead 25536 */ 25537 25538/** 25539 * Check if an element has a CSS class 25540 * 25541 * @borrows dom:hasElClass as videojs.hasClass 25542 * @deprecated Use videojs.dom.hasClass() instead 25543 */ 25544 25545/** 25546 * Add a CSS class name to an element 25547 * 25548 * @borrows dom:addElClass as videojs.addClass 25549 * @deprecated Use videojs.dom.addClass() instead 25550 */ 25551 25552/** 25553 * Remove a CSS class name from an element 25554 * 25555 * @borrows dom:removeElClass as videojs.removeClass 25556 * @deprecated Use videojs.dom.removeClass() instead 25557 */ 25558 25559/** 25560 * Adds or removes a CSS class name on an element depending on an optional 25561 * condition or the presence/absence of the class name. 25562 * 25563 * @borrows dom:toggleElClass as videojs.toggleClass 25564 * @deprecated Use videojs.dom.toggleClass() instead 25565 */ 25566 25567/** 25568 * Apply attributes to an HTML element. 25569 * 25570 * @borrows dom:setElAttributes as videojs.setAttribute 25571 * @deprecated Use videojs.dom.setAttributes() instead 25572 */ 25573 25574/** 25575 * Get an element's attribute values, as defined on the HTML tag 25576 * Attributes are not the same as properties. They're defined on the tag 25577 * or with setAttribute (which shouldn't be used with HTML) 25578 * This will return true or false for boolean attributes. 25579 * 25580 * @borrows dom:getElAttributes as videojs.getAttributes 25581 * @deprecated Use videojs.dom.getAttributes() instead 25582 */ 25583 25584/** 25585 * Empties the contents of an element. 25586 * 25587 * @borrows dom:emptyEl as videojs.emptyEl 25588 * @deprecated Use videojs.dom.emptyEl() instead 25589 */ 25590 25591/** 25592 * Normalizes and appends content to an element. 25593 * 25594 * The content for an element can be passed in multiple types and 25595 * combinations, whose behavior is as follows: 25596 * 25597 * - String 25598 * Normalized into a text node. 25599 * 25600 * - Element, TextNode 25601 * Passed through. 25602 * 25603 * - Array 25604 * A one-dimensional array of strings, elements, nodes, or functions (which 25605 * return single strings, elements, or nodes). 25606 * 25607 * - Function 25608 * If the sole argument, is expected to produce a string, element, 25609 * node, or array. 25610 * 25611 * @borrows dom:appendContents as videojs.appendContet 25612 * @deprecated Use videojs.dom.appendContent() instead 25613 */ 25614 25615/** 25616 * Normalizes and inserts content into an element; this is identical to 25617 * `appendContent()`, except it empties the element first. 25618 * 25619 * The content for an element can be passed in multiple types and 25620 * combinations, whose behavior is as follows: 25621 * 25622 * - String 25623 * Normalized into a text node. 25624 * 25625 * - Element, TextNode 25626 * Passed through. 25627 * 25628 * - Array 25629 * A one-dimensional array of strings, elements, nodes, or functions (which 25630 * return single strings, elements, or nodes). 25631 * 25632 * - Function 25633 * If the sole argument, is expected to produce a string, element, 25634 * node, or array. 25635 * 25636 * @borrows dom:insertContent as videojs.insertContent 25637 * @deprecated Use videojs.dom.insertContent() instead 25638 */ 25639['isEl', 'isTextNode', 'createEl', 'hasClass', 'addClass', 'removeClass', 'toggleClass', 'setAttributes', 'getAttributes', 'emptyEl', 'appendContent', 'insertContent'].forEach(function (k) { 25640 videojs[k] = function () { 25641 log$1.warn('videojs.' + k + '() is deprecated; use videojs.dom.' + k + '() instead'); 25642 return Dom[k].apply(null, arguments); 25643 }; 25644}); 25645 25646/** 25647 * A safe getComputedStyle with an IE8 fallback. 25648 * 25649 * This is because in Firefox, if the player is loaded in an iframe with `display:none`, 25650 * then `getComputedStyle` returns `null`, so, we do a null-check to make sure 25651 * that the player doesn't break in these cases. 25652 * See https://bugzilla.mozilla.org/show_bug.cgi?id=548397 for more details. 25653 * 25654 * @borrows computed-style:computedStyle as videojs.computedStyle 25655 */ 25656videojs.computedStyle = computedStyle; 25657 25658/** 25659 * Export the Dom utilities for use in external plugins 25660 * and Tech's 25661 */ 25662videojs.dom = Dom; 25663 25664/** 25665 * Export the Url utilities for use in external plugins 25666 * and Tech's 25667 */ 25668videojs.url = Url; 25669 25670return videojs; 25671 25672}))); 25673 25674!function(){!function(a){var b=a&&a.videojs;b&&(b.CDN_VERSION="6.6.3")}(window),function(a,b,c,d,e,f,g){b&&b.HELP_IMPROVE_VIDEOJS!==!1&&(e.random()>.01||(f=b.location,g=b.videojs||{},a.src="//www.google-analytics.com/__utm.gif?utmwv=5.4.2&utmac=UA-16505296-3&utmn=1&utmhn="+d(f.hostname)+"&utmsr="+b.screen.availWi
25674dth+"x"+b.screen.availHeight+"&utmul="+(c.language||c.userLanguage||"").toLowerCase()+"&utmr="+d(f.href)+"&utmp="+d(f.hostname+f.pathname)+"&utmcc=__utma%3D1."+e.floor(1e10*e.random())+".1.1.1.1%3B&utme=8(vjsv*cdnv)9("+g.VERSION+"*"+g.CDN_VERSION+")"))}(new Image,window,navigator,encodeURIComponent,Math)}();
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.