1/** 2 * @license 3 * Video.js 5.20.2 <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(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.videojs = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){ 14'use strict'; 15 16exports.__esModule = true; 17 18var _button = _dereq_(2); 19 20var _button2 = _interopRequireDefault(_button); 21 22var _component = _dereq_(5); 23 24var _component2 = _interopRequireDefault(_component); 25 26function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 27 28function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 29 30function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 31 32function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 33 * @file big-play-button.js 34 */ 35 36 37/** 38 * The initial play button that shows before the video has played. The hiding of the 39 * `BigPlayButton` get done via CSS and `Player` states. 40 * 41 * @extends Button 42 */ 43var BigPlayButton = function (_Button) { 44 _inherits(BigPlayButton, _Button); 45 46 function BigPlayButton(player, options) { 47 _classCallCheck(this, BigPlayButton); 48 49 var _this = _possibleConstructorReturn(this, _Button.call(this, player, options)); 50 51 _this.mouseused_ = false; 52 53 _this.on('mousedown', _this.handleMouseDown); 54 return _this; 55 } 56 57 /** 58 * Builds the default DOM `className`. 59 * 60 * @return {string} 61 * The DOM `className` for this object. Always returns 'vjs-big-play-button'. 62 */ 63 64 65 BigPlayButton.prototype.buildCSSClass = function buildCSSClass() { 66 return 'vjs-big-play-button'; 67 }; 68 69 /** 70 * This gets called when a `BigPlayButton` "clicked". See {@link ClickableComponent} 71 * for more detailed information on what a click can be. 72 * 73 * @param {EventTarget~Event} event
74 * The `keydown`, `tap`, or `click` event that caused this function to be 75 * called. 76 * 77 * @listens tap 78 * @listens click 79 */ 80 81 82 BigPlayButton.prototype.handleClick = function handleClick(event) { 83 var playPromise = this.player_.play(); 84 85 // exit early if clicked via the mouse 86 if (this.mouseused_ && event.clientX && event.clientY) { 87 return; 88 } 89 90 var cb = this.player_.getChild('controlBar'); 91 var playToggle = cb && cb.getChild('playToggle'); 92 93 if (!playToggle) { 94 this.player_.focus(); 95 return; 96 } 97 98 var playFocus = function playFocus() { 99 return playToggle.focus(); 100 }; 101 102 if (playPromise && playPromise.then) { 103 var ignoreRejectedPlayPromise = function ignoreRejectedPlayPromise() {}; 104 105 playPromise.then(playFocus, ignoreRejectedPlayPromise); 106 } else { 107 this.setTimeout(playFocus, 1); 108 } 109 }; 110 111 BigPlayButton.prototype.handleKeyPress = function handleKeyPress(event) { 112 this.mouseused_ = false; 113 114 _Button.prototype.handleKeyPress.call(this, event); 115 }; 116 117 BigPlayButton.prototype.handleMouseDown = function handleMouseDown(event) { 118 this.mouseused_ = true; 119 }; 120 121 return BigPlayButton; 122}(_button2['default']); 123 124/** 125 * The text that should display over the `BigPlayButton`s controls. Added to for localization. 126 * 127 * @type {string} 128 * @private 129 */ 130 131 132BigPlayButton.prototype.controlText_ = 'Play Video'; 133 134_component2['default'].registerComponent('BigPlayButton', BigPlayButton); 135exports['default'] = BigPlayButton; 136 137},{"2":2,"5":5}],2:[function(_dereq_,module,exports){ 138'use strict'; 139 140exports.__esModule = true; 141 142var _clickableComponent = _dereq_(3); 143 144var _clickableComponent2 = _interopRequireDefault(_clickableComponent); 145 146var _component = _dereq_(5); 147 148var _component2 = _interopRequireDefault(_component); 149 150var _log = _dereq_(86); 151 152var _log2 = _interopRequireDefault(_log); 153 154var _obj = _dereq_(88); 155 156function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 157 158function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 159 160function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 161 162function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 163 * @file button.js 164 */ 165 166 167/** 168 * Base class for all buttons. 169 * 170 * @extends ClickableComponent 171 */ 172var Button = function (_ClickableComponent) { 173 _inherits(Button, _ClickableComponent); 174 175 function Button() { 176 _classCallCheck(this, Button); 177 178 return _possibleConstructorReturn(this, _ClickableComponent.apply(this, arguments)); 179 } 180 181 /** 182 * Create the `Button`s DOM element. 183 * 184 * @param {string} [tag=button] 185 * Element's node type. e.g. 'button' 186 * 187 * @param {Object} [props={}] 188 * An object of properties that should be set on the element. 189 * 190 * @param {Object} [attributes={}] 191 * An object of attributes that should be set on the element. 192 * 193 * @return {Element} 194 * The element that gets created. 195 */ 196 Button.prototype.createEl = function createEl() { 197 var tag = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'button'; 198 var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 199 var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; 200 201 props = (0, _obj.assign)({ 202 className: this.buildCSSClass() 203 }, props); 204 205 if (tag !== 'button') { 206 _log2['default'].warn('Creating a Button with an HTML element of ' + tag + ' is deprecated; use ClickableComponent instead.'); 207 208 // Add properties for clickable element which is not a native HTML button 209 props = (0, _obj.assign)({ 210 tabIndex: 0 211 }, props); 212 213 // Add ARIA attributes for clickable element which is not a native HTML button 214 attributes = (0, _obj.assign)({ 215 role: 'button' 216 }, attributes); 217 } 218 219 // Add attributes for button element 220 attributes = (0, _obj.assign)({ 221 222 // Necessary since the default button type is "submit" 223 'type': 'button', 224 225 // let the screen reader user know that the text of the button may change 226 'aria-live': 'polite' 227 }, attributes); 228 229 var el = _component2['default'].prototype.createEl.call(this, tag, props, attributes); 230 231 this.createControlTextEl(el); 232 233 return el; 234 }; 235 236 /** 237 * Add a child `Component` inside of this `Button`. 238 * 239 * @param {string|Component} child 240 * The name or instance of a child to add. 241 * 242 * @param {Object} [options={}] 243 * The key/value store of options that will get passed to children of 244 * the child. 245 * 246 * @return {Component} 247 * The `Component` that gets added as a child. When using a string the 248 * `Component` will get created by this process. 249 * 250 * @deprecated since version 5 251 */ 252 253 254 Button.prototype.addChild = function addChild(child) { 255 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 256 257 var className = this.constructor.name; 258 259 _log2['default'].warn('Adding an actionable (user controllable) child to a Button (' + className + ') is not supported; use a ClickableComponent instead.'); 260 261 // Avoid the error message generated by ClickableComponent's addChild method 262 return _component2['default'].prototype.addChild.call(this, child, options); 263 }; 264 265 /** 266 * Enable the `Button` element so that it can be activated or clicked. Use this with 267 * {@link Button#disable}. 268 */ 269 270 271 Button.prototype.enable = function enable() { 272 _ClickableComponent.prototype.enable.call(this); 273 this.el_.removeAttribute('disabled'); 274 }; 275 276 /** 277 * Enable the `Button` element so that it cannot be activated or clicked. Use this with 278 * {@link Button#enable}. 279 */ 280 281 282 Button.prototype.disable = function disable() { 283 _ClickableComponent.prototype.disable.call(this); 284 this.el_.setAttribute('disabled', 'disabled'); 285 }; 286 287 /** 288 * This gets called when a `Button` has focus and `keydown` is triggered via a key 289 * press. 290 * 291 * @param {EventTarget~Event} event 292 * The event that caused this function to get called. 293 * 294 * @listens keydown 295 */ 296 297 298 Button.prototype.handleKeyPress = function handleKeyPress(event) { 299 300 // Ignore Space (32) or Enter (13) key operation, which is handled by the browser for a button. 301 if (event.which === 32 || event.which === 13) { 302 return; 303 } 304 305 // Pass keypress handling up for unsupported keys 306 _ClickableComponent.prototype.handleKeyPress.call(this, event); 307 }; 308 309 return Button; 310}(_clickableComponent2['default']); 311 312_component2['default'].registerComponent('Button', Button); 313exports['default'] = Button; 314 315},{"3":3,"5":5,"86":86,"88":88}],3:[function(_dereq_,module,exports){ 316'use strict'; 317 318exports.__esModule = true; 319 320var _component = _dereq_(5); 321 322var _component2 = _interopRequireDefault(_component); 323 324var _dom = _dereq_(81); 325 326var Dom = _interopRequireWildcard(_dom); 327 328var _events = _dereq_(82); 329 330var Events = _interopRequireWildcard(_events); 331 332var _fn = _dereq_(83); 333 334var Fn = _interopRequireWildcard(_fn); 335 336var _log = _dereq_(86); 337 338var _log2 = _interopRequireDefault(_log); 339 340var _document = _dereq_(94); 341 342var _document2 = _interopRequireDefault(_document); 343 344var _obj = _dereq_(88); 345 346function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 347 348function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 349 350function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 351 352function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 353 354function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
354onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 355 * @file button.js 356 */ 357 358 359/** 360 * Clickable Component which is clickable or keyboard actionable, 361 * but is not a native HTML button. 362 * 363 * @extends Component 364 */ 365var ClickableComponent = function (_Component) { 366 _inherits(ClickableComponent, _Component); 367 368 /** 369 * Creates an instance of this class. 370 * 371 * @param {Player} player 372 * The `Player` that this class should be attached to. 373 * 374 * @param {Object} [options] 375 * The key/value store of player options. 376 */ 377 function ClickableComponent(player, options) { 378 _classCallCheck(this, ClickableComponent); 379 380 var _this = _possibleConstructorReturn(this, _Component.call(this, player, options)); 381 382 _this.emitTapEvents(); 383 384 _this.enable(); 385 return _this; 386 } 387 388 /** 389 * Create the `Component`s DOM element. 390 * 391 * @param {string} [tag=div] 392 * The element's node type. 393 * 394 * @param {Object} [props={}] 395 * An object of properties that should be set on the element. 396 * 397 * @param {Object} [attributes={}] 398 * An object of attributes that should be set on the element. 399 * 400 * @return {Element} 401 * The element that gets created. 402 */ 403 404 405 ClickableComponent.prototype.createEl = function createEl() { 406 var tag = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'div'; 407 var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 408 var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; 409 410 props = (0, _obj.assign)({ 411 className: this.buildCSSClass(), 412 tabIndex: 0 413 }, props); 414 415 if (tag === 'button') { 416 _log2['default'].error('Creating a ClickableComponent with an HTML element of ' + tag + ' is not supported; use a Button instead.'); 417 } 418 419 // Add ARIA attributes for clickable element which is not a native HTML button 420 attributes = (0, _obj.assign)({ 421 'role': 'button', 422 423 // let the screen reader user know that the text of the element may change 424 'aria-live': 'polite' 425 }, attributes); 426 427 this.tabIndex_ = props.tabIndex; 428 429 var el = _Component.prototype.createEl.call(this, tag, props, attributes); 430 431 this.createControlTextEl(el); 432 433 return el; 434 }; 435 436 /** 437 * Create a control text element on this `Component` 438 * 439 * @param {Element} [el] 440 * Parent element for the control text. 441 * 442 * @return {Element} 443 * The control text element that gets created. 444 */ 445 446 447 ClickableComponent.prototype.createControlTextEl = function createControlTextEl(el) { 448 this.controlTextEl_ = Dom.createEl('span', { 449 className: 'vjs-control-text' 450 }); 451 452 if (el) { 453 el.appendChild(this.controlTextEl_); 454 } 455 456 this.controlText(this.controlText_, el); 457 458 return this.controlTextEl_; 459 }; 460 461 /** 462 * Get or set the localize text to use for the controls on the `Component`. 463 * 464 * @param {string} [text] 465 * Control text for element. 466 * 467 * @param {Element} [el=this.el()] 468 * Element to set the title on. 469 * 470 * @return {string|ClickableComponent} 471 * - The control text when getting 472 * - Returns itself when setting; method can be chained. 473 */ 474 475 476 ClickableComponent.prototype.controlText = function controlText(text) { 477 var el = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.el(); 478 479 if (!text) { 480 return this.controlText_ || 'Need Text'; 481 } 482 483 var localizedText = this.localize(text); 484 485 this.controlText_ = text; 486 this.controlTextEl_.innerHTML = localizedText; 487 488 if (!this.nonIconControl) { 489 // Set title attribute if only an icon is shown 490 el.setAttribute('title', localizedText); 491 } 492 493 return this; 494 }; 495 496 /** 497 * Builds the default DOM `className`. 498 * 499 * @return {string} 500 * The DOM `className` for this object. 501 */ 502 503 504 ClickableComponent.prototype.buildCSSClass = function buildCSSClass() { 505 return 'vjs-control vjs-button ' + _Component.prototype.buildCSSClass.call(this); 506 }; 507 508 /** 509 * Enable this `Component`s element. 510 * 511 * @return {ClickableComponent} 512 * Returns itself; method can be chained. 513 */ 514 515 516 ClickableComponent.prototype.enable = function enable() { 517 this.removeClass('vjs-disabled'); 518 this.el_.setAttribute('aria-disabled', 'false'); 519 if (typeof this.tabIndex_ !== 'undefined') { 520 this.el_.setAttribute('tabIndex', this.tabIndex_); 521 } 522 this.off(['tap', 'click'], this.handleClick); 523 this.off('focus', this.handleFocus); 524 this.off('blur', this.handleBlur); 525 this.on(['tap', 'click'], this.handleClick); 526 this.on('focus', this.handleFocus); 527 this.on('blur', this.handleBlur); 528 return this; 529 }; 530 531 /** 532 * Disable this `Component`s element. 533 * 534 * @return {ClickableComponent} 535 * Returns itself; method can be chained. 536 */ 537 538 539 ClickableComponent.prototype.disable = function disable() { 540 this.addClass('vjs-disabled'); 541 this.el_.setAttribute('aria-disabled', 'true'); 542 if (typeof this.tabIndex_ !== 'undefined') { 543 this.el_.removeAttribute('tabIndex'); 544 } 545 this.off(['tap', 'click'], this.handleClick); 546 this.off('focus', this.handleFocus); 547 this.off('blur', this.handleBlur); 548 return this; 549 }; 550 551 /** 552 * This gets called when a `ClickableComponent` gets: 553 * - Clicked (via the `click` event, listening starts in the constructor) 554 * - Tapped (via the `tap` event, listening starts in the constructor) 555 * - The following things happen in order: 556 * 1. {@link ClickableComponent#handleFocus} is called via a `focus` event on the 557 * `ClickableComponent`. 558 * 2. {@link ClickableComponent#handleFocus} adds a listener for `keydown` on using 559 * {@link ClickableComponent#handleKeyPress}. 560 * 3. `ClickableComponent` has not had a `blur` event (`blur` means that focus was lost). The user presses 561 * the space or enter key. 562 * 4. {@link ClickableComponent#handleKeyPress} calls this function with the `keydown` 563 * event as a parameter. 564 * 565 * @param {EventTarget~Event} event
566 * The `keydown`, `tap`, or `click` event that caused this function to be 567 * called. 568 * 569 * @listens tap 570 * @listens click 571 * @abstract 572 */ 573 574 575 ClickableComponent.prototype.handleClick = function handleClick(event) {}; 576 577 /** 578 * This gets called when a `ClickableComponent` gains focus via a `focus` event. 579 * Turns on listening for `keydown` events. When they happen it 580 * calls `this.handleKeyPress`. 581 * 582 * @param {EventTarget~Event} event 583 * The `focus` event that caused this function to be called. 584 * 585 * @listens focus 586 */ 587 588 589 ClickableComponent.prototype.handleFocus = function handleFocus(event) { 590 Events.on(_document2['default'], 'keydown', Fn.bind(this, this.handleKeyPress)); 591 }; 592 593 /** 594 * Called when this ClickableComponent has focus and a key gets pressed down. By 595 * default it will call `this.handleClick` when the key is space or enter. 596 * 597 * @param {EventTarget~Event} event 598 * The `keydown` event that caused this function to be called. 599 * 600 * @listens keydown 601 */ 602 603 604 ClickableComponent.prototype.handleKeyPress = function handleKeyPress(event) { 605 606 // Support Space (32) or Enter (13) key operation to fire a click event 607 if (event.which === 32 || event.which === 13) { 608 event.preventDefault(); 609 this.handleClick(event); 610 } else if (_Component.prototype.handleKeyPress) { 611 612 // Pass keypress handling up for unsupported keys 613 _Component.prototype.handleKeyPress.call(this, event); 614 } 615 }; 616 617 /** 618 * Called when a `ClickableComponent` loses focus. Turns off the listener for 619 * `keydown` events. Which Stops `this.handleKeyPress` from getting called. 620 * 621 * @param {EventTarget~Event} event 622 * The `blur` event that caused this function to be called. 623 * 624 * @listens blur 625 */ 626 627 628 ClickableComponent.prototype.handleBlur = function handleBlur(event) { 629 Events.off(_document2['default'], 'keydown', Fn.bind(this, this.handleKeyPress)); 630 }; 631 632 return ClickableComponent; 633}(_component2['default']); 634 635_component2['default'].registerComponent('ClickableComponent', ClickableComponent); 636exports['default'] = ClickableComponent; 637 638},{"5":5,"81":81,"82":82,"83":83,"86":86,"88":88,"94":94}],4:[function(_dereq_,module,exports){ 639'use strict'; 640 641exports.__esModule = true; 642 643var _button = _dereq_(2); 644 645var _button2 = _interopRequireDefault(_button); 646 647var _component = _dereq_(5); 648 649var _component2 = _interopRequireDefault(_component); 650 651function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 652 653function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 654 655function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 656 657function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 658 * @file close-button.js 659 */ 660 661 662/** 663 * The `CloseButton` is a `{@link Button}` that fires a `close` event when 664 * it gets clicked. 665 * 666 * @extends Button 667 */ 668var CloseButton = function (_Button) { 669 _inherits(CloseButton, _Button); 670 671 /** 672 * Creates an instance of the this class. 673 * 674 * @param {Player} player 675 * The `Player` that this class should be attached to. 676 * 677 * @param {Object} [options] 678 * The key/value store of player options. 679 */ 680 function CloseButton(player, options) { 681 _classCallCheck(this, CloseButton); 682 683 var _this = _possibleConstructorReturn(this, _Button.call(this, player, options)); 684 685 _this.controlText(options && options.controlText || _this.localize('Close')); 686 return _this; 687 } 688 689 /** 690 * Builds the default DOM `className`. 691 * 692 * @return {string} 693 * The DOM `className` for this object. 694 */ 695 696 697 CloseButton.prototype.buildCSSClass = function buildCSSClass() { 698 return 'vjs-close-button ' + _Button.prototype.buildCSSClass.call(this); 699 }; 700 701 /** 702 * This gets called when a `CloseButton` gets clicked. See 703 * {@link ClickableComponent#handleClick} for more information on when this will be 704 * triggered 705 * 706 * @param {EventTarget~Event} event
707 * The `keydown`, `tap`, or `click` event that caused this function to be 708 * called. 709 * 710 * @listens tap 711 * @listens click 712 * @fires CloseButton#close 713 */ 714 715 716 CloseButton.prototype.handleClick = function handleClick(event) { 717 718 /** 719 * Triggered when the a `CloseButton` is clicked. 720 * 721 * @event CloseButton#close 722 * @type {EventTarget~Event} 723 * 724 * @property {boolean} [bubbles=false] 725 * set to false so that the close event does not 726 * bubble up to parents if there is no listener 727 */ 728 this.trigger({ type: 'close', bubbles: false }); 729 }; 730 731 return CloseButton; 732}(_button2['default']); 733 734_component2['default'].registerComponent('CloseButton', CloseButton); 735exports['default'] = CloseButton; 736 737},{"2":2,"5":5}],5:[function(_dereq_,module,exports){ 738'use strict'; 739 740exports.__esModule = true; 741 742var _window = _dereq_(95); 743 744var _window2 = _interopRequireDefault(_window); 745 746var _dom = _dereq_(81); 747 748var Dom = _interopRequireWildcard(_dom); 749 750var _fn = _dereq_(83); 751 752var Fn = _interopRequireWildcard(_fn); 753 754var _guid = _dereq_(85); 755 756var Guid = _interopRequireWildcard(_guid); 757 758var _events = _dereq_(82); 759 760var Events = _interopRequireWildcard(_events); 761 762var _log = _dereq_(86); 763 764var _log2 = _interopRequireDefault(_log); 765 766var _toTitleCase = _dereq_(91); 767 768var _toTitleCase2 = _interopRequireDefault(_toTitleCase); 769 770var _mergeOptions = _dereq_(87); 771 772var _mergeOptions2 = _interopRequireDefault(_mergeOptions); 773 774function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 775 776function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 777 778function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** 779 * Player Component - Base class for all UI objects 780 * 781 * @file component.js 782 */ 783 784 785/** 786 * Base class for all UI Components. 787 * Components are UI objects which represent both a javascript object and an element 788 * in the DOM. They can be children of other components, and can have 789 * children themselves. 790 * 791 * Components can also use methods from {@link EventTarget} 792 */ 793var Component = function () { 794 795 /** 796 * A callback that is called when a component is ready. Does not have any 797 * paramters and any callback value will be ignored. 798 * 799 * @callback Component~ReadyCallback 800 * @this Component 801 */ 802 803 /** 804 * Creates an instance of this class. 805 * 806 * @param {Player} player 807 * The `Player` that this class should be attached to. 808 * 809 * @param {Object} [options] 810 * The key/value store of player options. 811 # 812 * @param {Object[]} [options.children] 813 * An array of children objects to intialize this component with. Children objects have 814 * a name property that will be used if more than one component of the same type needs to be 815 * added. 816 * 817 * @param {Component~ReadyCallback} [ready] 818 * Function that gets called when the `Component` is ready. 819 */ 820 function Component(player, options, ready) { 821 _classCallCheck(this, Component); 822 823 // The component might be the player itself and we can't pass `this` to super 824 if (!player && this.play) { 825 this.player_ = player = this; // eslint-disable-line 826 } else { 827 this.player_ = player; 828 } 829 830 // Make a copy of prototype.options_ to protect against overriding defaults 831 this.options_ = (0, _mergeOptions2['default'])({}, this.options_); 832 833 // Updated options with supplied options 834 options = this.options_ = (0, _mergeOptions2['default'])(this.options_, options); 835 836 // Get ID from options or options element if one is supplied 837 this.id_ = options.id || options.el && options.el.id; 838 839 // If there was no ID from the options, generate one 840 if (!this.id_) { 841 // Don't require the player ID function in the case of mock players 842 var id = player && player.id && player.id() || 'no_player'; 843 844 this.id_ = id + '_component_' + Guid.newGUID(); 845 } 846 847 this.name_ = options.name || null; 848 849 // Create element if one wasn't provided in options 850 if (options.el) { 851 this.el_ = options.el; 852 } else if (options.createEl !== false) { 853 this.el_ = this.createEl(); 854 } 855 856 this.children_ = []; 857 this.childIndex_ = {}; 858 this.childNameIndex_ = {}; 859 860 // Add any child components in options 861 if (options.initChildren !== false) { 862 this.initChildren(); 863 } 864 865 this.ready(ready); 866 // Don't want to trigger ready here or it will before init is actually 867 // finished for all children that run this constructor 868 869 if (options.reportTouchActivity !== false) { 870 this.enableTouchActivity(); 871 } 872 } 873 874 /** 875 * Dispose of the `Component` and all child components. 876 * 877 * @fires Component#dispose 878 */ 879 880 881 Component.prototype.dispose = function dispose() { 882 883 /** 884 * Triggered when a `Component` is disposed. 885 * 886 * @event Component#dispose 887 * @type {EventTarget~Event} 888 * 889 * @property {boolean} [bubbles=false] 890 * set to false so that the close event does not 891 * bubble up 892 */ 893 this.trigger({ type: 'dispose', bubbles: false }); 894 895 // Dispose all children. 896 if (this.children_) { 897 for (var i = this.children_.length - 1; i >= 0; i--) { 898 if (this.children_[i].dispose) { 899 this.children_[i].dispose(); 900 } 901 } 902 } 903 904 // Delete child references 905 this.children_ = null; 906 this.childIndex_ = null; 907 this.childNameIndex_ = null; 908 909 // Remove all event listeners. 910 this.off(); 911 912 // Remove element from DOM 913 if (this.el_.parentNode) { 914 this.el_.parentNode.removeChild(this.el_); 915 } 916 917 Dom.removeElData(this.el_); 918 this.el_ = null; 919 }; 920 921 /** 922 * Return the {@link Player}
922 that the `Component` has attached to. 923 * 924 * @return {Player} 925 * The player that this `Component` has attached to. 926 */ 927 928 929 Component.prototype.player = function player() { 930 return this.player_; 931 }; 932 933 /** 934 * Deep merge of options objects with new options. 935 * > Note: When both `obj` and `options` contain properties whose values are objects. 936 * The two properties get merged using {@link module:mergeOptions} 937 * 938 * @param {Object} obj 939 * The object that contains new options. 940 * 941 * @return {Object} 942 * A new object of `this.options_` and `obj` merged together. 943 * 944 * @deprecated since version 5 945 */ 946 947 948 Component.prototype.options = function options(obj) { 949 _log2['default'].warn('this.options() has been deprecated and will be moved to the constructor in 6.0'); 950 951 if (!obj) { 952 return this.options_; 953 } 954 955 this.options_ = (0, _mergeOptions2['default'])(this.options_, obj); 956 return this.options_; 957 }; 958 959 /** 960 * Get the `Component`s DOM element 961 * 962 * @return {Element} 963 * The DOM element for this `Component`. 964 */ 965 966 967 Component.prototype.el = function el() { 968 return this.el_; 969 }; 970 971 /** 972 * Create the `Component`s DOM element. 973 * 974 * @param {string} [tagName] 975 * Element's DOM node type. e.g. 'div' 976 * 977 * @param {Object} [properties] 978 * An object of properties that should be set. 979 * 980 * @param {Object} [attributes] 981 * An object of attributes that should be set. 982 * 983 * @return {Element} 984 * The element that gets created. 985 */ 986 987 988 Component.prototype.createEl = function createEl(tagName, properties, attributes) { 989 return Dom.createEl(tagName, properties, attributes); 990 }; 991 992 /** 993 * Localize a string given the string in english. 994 * 995 * @param {string} string 996 * The string to localize. 997 * 998 * @return {string} 999 * The localized string or if no localization exists the english string. 1000 */ 1001 1002 1003 Component.prototype.localize = function localize(string) { 1004 var code = this.player_.language && this.player_.language(); 1005 var languages = this.player_.languages && this.player_.languages(); 1006 1007 if (!code || !languages) { 1008 return string; 1009 } 1010 1011 var language = languages[code]; 1012 1013 if (language && language[string]) { 1014 return language[string]; 1015 } 1016 1017 var primaryCode = code.split('-')[0]; 1018 var primaryLang = languages[primaryCode]; 1019 1020 if (primaryLang && primaryLang[string]) { 1021 return primaryLang[string]; 1022 } 1023 1024 return string; 1025 }; 1026 1027 /** 1028 * Return the `Component`s DOM element. This is where children get inserted. 1029 * This will usually be the the same as the element returned in {@link Component#el}. 1030 * 1031 * @return {Element} 1032 * The content element for this `Component`. 1033 */ 1034 1035 1036 Component.prototype.contentEl = function contentEl() { 1037 return this.contentEl_ || this.el_; 1038 }; 1039 1040 /** 1041 * Get this `Component`s ID 1042 * 1043 * @return {string} 1044 * The id of this `Component` 1045 */ 1046 1047 1048 Component.prototype.id = function id() { 1049 return this.id_; 1050 }; 1051 1052 /** 1053 * Get the `Component`s name. The name gets used to reference the `Component` 1054 * and is set during registration. 1055 * 1056 * @return {string} 1057 * The name of this `Component`. 1058 */ 1059 1060 1061 Component.prototype.name = function name() { 1062 return this.name_; 1063 }; 1064 1065 /** 1066 * Get an array of all child components 1067 * 1068 * @return {Array} 1069 * The children 1070 */ 1071 1072 1073 Component.prototype.children = function children() { 1074 return this.children_; 1075 }; 1076 1077 /** 1078 * Returns the child `Component` with the given `id`. 1079 * 1080 * @param {string} id 1081 * The id of the child `Component` to get. 1082 * 1083 * @return {Component|undefined} 1084 * The child `Component` with the given `id` or undefined. 1085 */ 1086 1087 1088 Component.prototype.getChildById = function getChildById(id) { 1089 return this.childIndex_[id]; 1090 }; 1091 1092 /** 1093 * Returns the child `Component` with the given `name`. 1094 * 1095 * @param {string} name 1096 * The name of the child `Component` to get. 1097 * 1098 * @return {Component|undefined} 1099 * The child `Component` with the given `name` or undefined. 1100 */ 1101 1102 1103 Component.prototype.getChild = function getChild(name) { 1104 if (!name) { 1105 return; 1106 } 1107 1108 name = (0, _toTitleCase2['default'])(name); 1109 1110 return this.childNameIndex_[name]; 1111 }; 1112 1113 /** 1114 * Add a child `Component` inside the current `Component`. 1115 * 1116 * 1117 * @param {string|Component} child 1118 * The name or instance of a child to add. 1119 * 1120 * @param {Object} [options={}] 1121 * The key/value store of options that will get passed to children of 1122 * the child. 1123 * 1124 * @param {number} [index=this.children_.length] 1125 * The index to attempt to add a child into. 1126 * 1127 * @return {Component} 1128 * The `Component` that gets added as a child. When using a string the 1129 * `Component` will get created by this process. 1130 */ 1131 1132 1133 Component.prototype.addChild = function addChild(child) { 1134 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 1135 var index = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.children_.length; 1136 1137 var component = void 0; 1138 var componentName = void 0; 1139 1140 // If child is a string, create component with options 1141 if (typeof child === 'string') { 1142 componentName = (0, _toTitleCase2['default'])(child); 1143 1144 // Options can also be specified as a boolean, 1145 // so convert to an empty object if false. 1146 if (!options) { 1147 options = {}; 1148 } 1149 1150 // Same as above, but true is deprecated so show a warning. 1151 if (options === true) { 1152 _log2['default'].warn('Initializing a child component with `true` is deprecated.' + 'Children should be defined in an array when possible, ' + 'but if necessary use an object instead of `true`.'); 1153 options = {}; 1154 } 1155 1156 var componentClassName = options.componentClass || componentName; 1157 1158 // Set name through options 1159 options.name = componentName; 1160 1161 // Create a new object & element for this controls set 1162 // If there's no .player_, this is a player 1163 var ComponentClass = Component.getComponent(componentClassName); 1164 1165 if (!ComponentClass) { 1166 throw new Error('Component ' + componentClassName + ' does not exist'); 1167 } 1168 1169 // data stored directly on the videojs object may be 1170 // misidentified as a component to retain 1171 // backwards-compatibility with 4.x. check to make sure the 1172 // component class can be instantiated. 1173 if (typeof ComponentClass !== 'function') { 1174 return null; 1175 } 1176 1177 component = new ComponentClass(this.player_ || this, options); 1178 1179 // child is a component instance 1180 } else { 1181 component = child; 1182 } 1183 1184 this.children_.splice(index, 0, component); 1185 1186 if (typeof component.id === 'function') { 1187 this.childIndex_[component.id()] = component; 1188 } 1189 1190 // If a name wasn't used to create the component, check if we can use the 1191 // name function of the component 1192 componentName = componentName || component.name && (0, _toTitleCase2['default'])(component.name()); 1193 1194 if (componentName) { 1195 this.childNameIndex_[componentName] = component; 1196 } 1197 1198 // Add the UI object's element to the container div (box) 1199 // Having an element is not required 1200 if (typeof component.el === 'function' && component.el()) { 1201 var childNodes = this.contentEl().children;
vendor: 4,344 bytes, lines 1202-1346
1202 var refNode = childNodes[index] || null; 1203 1204 this.contentEl().insertBefore(component.el(), refNode); 1205 } 1206 1207 // Return so it can stored on parent object if desired. 1208 return component; 1209 }; 1210 1211 /** 1212 * Remove a child `Component` from this `Component`s list of children. Also removes 1213 * the child `Component`s element from this `Component`s element. 1214 * 1215 * @param {Component} component 1216 * The child `Component` to remove. 1217 */ 1218 1219 1220 Component.prototype.removeChild = function removeChild(component) { 1221 if (typeof component === 'string') { 1222 component = this.getChild(component); 1223 } 1224 1225 if (!component || !this.children_) { 1226 return; 1227 } 1228 1229 var childFound = false; 1230 1231 for (var i = this.children_.length - 1; i >= 0; i--) { 1232 if (this.children_[i] === component) { 1233 childFound = true; 1234 this.children_.splice(i, 1); 1235 break; 1236 } 1237 } 1238 1239 if (!childFound) { 1240 return; 1241 } 1242 1243 this.childIndex_[component.id()] = null; 1244 this.childNameIndex_[component.name()] = null; 1245 1246 var compEl = component.el(); 1247 1248 if (compEl && compEl.parentNode === this.contentEl()) { 1249 this.contentEl().removeChild(component.el()); 1250 } 1251 }; 1252 1253 /** 1254 * Add and initialize default child `Component`s based upon options. 1255 */ 1256 1257 1258 Component.prototype.initChildren = function initChildren() { 1259 var _this = this; 1260 1261 var children = this.options_.children; 1262 1263 if (children) { 1264 // `this` is `parent` 1265 var parentOptions = this.options_; 1266 1267 var handleAdd = function handleAdd(child) { 1268 var name = child.name; 1269 var opts = child.opts; 1270 1271 // Allow options for children to be set at the parent options 1272 // e.g. videojs(id, { controlBar: false }); 1273 // instead of videojs(id, { children: { controlBar: false }); 1274 if (parentOptions[name] !== undefined) { 1275 opts = parentOptions[name]; 1276 } 1277 1278 // Allow for disabling default components 1279 // e.g. options['children']['posterImage'] = false 1280 if (opts === false) { 1281 return; 1282 } 1283 1284 // Allow options to be passed as a simple boolean if no configuration 1285 // is necessary. 1286 if (opts === true) { 1287 opts = {}; 1288 } 1289 1290 // We also want to pass the original player options 1291 // to each component as well so they don't need to 1292 // reach back into the player for options later. 1293 opts.playerOptions = _this.options_.playerOptions; 1294 1295 // Create and add the child component. 1296 // Add a direct reference to the child by name on the parent instance. 1297 // If two of the same component are used, different names should be supplied 1298 // for each 1299 var newChild = _this.addChild(name, opts); 1300 1301 if (newChild) { 1302 _this[name] = newChild; 1303 } 1304 }; 1305 1306 // Allow for an array of children details to passed in the options 1307 var workingChildren = void 0; 1308 var Tech = Component.getComponent('Tech'); 1309 1310 if (Array.isArray(children)) { 1311 workingChildren = children; 1312 } else { 1313 workingChildren = Object.keys(children); 1314 } 1315 1316 workingChildren 1317 // children that are in this.options_ but also in workingChildren would 1318 // give us extra children we do not want. So, we want to filter them out. 1319 .concat(Object.keys(this.options_).filter(function (child) { 1320 return !workingChildren.some(function (wchild) { 1321 if (typeof wchild === 'string') { 1322 return child === wchild; 1323 } 1324 return child === wchild.name; 1325 }); 1326 })).map(function (child) { 1327 var name = void 0; 1328 var opts = void 0; 1329 1330 if (typeof child === 'string') { 1331 name = child; 1332 opts = children[name] || _this.options_[name] || {}; 1333 } else { 1334 name = child.name; 1335 opts = child; 1336 } 1337 1338 return { name: name, opts: opts }; 1339 }).filter(function (child) { 1340 // we have to make sure that child.name isn't in the techOrder since 1341 // techs are registerd as Components but can't aren't compatible 1342 // See https://github.com/videojs/video.js/issues/2772 1343 var c = Component.getComponent(child.opts.componentClass || (0, _toTitleCase2['default'])(child.name)); 1344 1345 return c && !Tech.isTech(c); 1346 }
1346).forEach(handleAdd); 1347 } 1348 }; 1349 1350 /** 1351 * Builds the default DOM class name. Should be overriden by sub-components. 1352 * 1353 * @return {string} 1354 * The DOM class name for this object. 1355 * 1356 * @abstract 1357 */ 1358 1359 1360 Component.prototype.buildCSSClass = function buildCSSClass() { 1361 // Child classes can include a function that does: 1362 // return 'CLASS NAME' + this._super(); 1363 return ''; 1364 }; 1365 1366 /** 1367 * Add an `event listener` to this `Component`s element. 1368 * 1369 * The benefit of using this over the following: 1370 * - `VjsEvents.on(otherElement, 'eventName', myFunc)` 1371 * - `otherComponent.on('eventName', myFunc)` 1372 * 1373 * 1. Is that the listeners will get cleaned up when either component gets disposed. 1374 * 1. It will also bind `myComponent` as the context of `myFunc`. 1375 * > NOTE: If you remove the element from the DOM that has used `on` you need to 1376 * clean up references using: `myComponent.trigger(el, 'dispose')` 1377 * This will also allow the browser to garbage collect it. In special 1378 * cases such as with `window` and `document`, which are both permanent, 1379 * this is not necessary. 1380 * 1381 * @param {string|Component|string[]} [first] 1382 * The event name, and array of event names, or another `Component`. 1383 * 1384 * @param {EventTarget~EventListener|string|string[]} [second] 1385 * The listener function, an event name, or an Array of events names. 1386 * 1387 * @param {EventTarget~EventListener} [third] 1388 * The event handler if `first` is a `Component` and `second` is an event name 1389 * or an Array of event names. 1390 * 1391 * @return {Component} 1392 * Returns itself; method can be chained. 1393 * 1394 * @listens Component#dispose 1395 */ 1396 1397 1398 Component.prototype.on = function on(first, second, third) { 1399 var _this2 = this; 1400 1401 if (typeof first === 'string' || Array.isArray(first)) { 1402 Events.on(this.el_, first, Fn.bind(this, second)); 1403 1404 // Targeting another component or element 1405 } else { 1406 var target = first; 1407 var type = second; 1408 var fn = Fn.bind(this, third); 1409 1410 // When this component is disposed, remove the listener from the other component 1411 var removeOnDispose = function removeOnDispose() { 1412 return _this2.off(target, type, fn); 1413 }; 1414 1415 // Use the same function ID so we can remove it later it using the ID 1416 // of the original listener 1417 removeOnDispose.guid = fn.guid; 1418 this.on('dispose', removeOnDispose); 1419 1420 // If the other component is disposed first we need to clean the reference 1421 // to the other component in this component's removeOnDispose listener 1422 // Otherwise we create a memory leak. 1423 var cleanRemover = function cleanRemover() { 1424 return _this2.off('dispose', removeOnDispose); 1425 }; 1426 1427 // Add the same function ID so we can easily remove it later 1428 cleanRemover.guid = fn.guid; 1429 1430 // Check if this is a DOM node 1431 if (first.nodeName) { 1432 // Add the listener to the other element 1433 Events.on(target, type, fn); 1434 Events.on(target, 'dispose', cleanRemover); 1435 1436 // Should be a component 1437 // Not using `instanceof Component` because it makes mock players difficult 1438 } else if (typeof first.on === 'function') { 1439 // Add the listener to the other component 1440 target.on(type, fn); 1441 target.on('dispose', cleanRemover); 1442 } 1443 } 1444 1445 return this; 1446 }; 1447 1448 /** 1449 * Remove an event listener from this `Component`s element. If the second argument is 1450 * exluded all listeners for the type passed in as the first argument will be removed. 1451 * 1452 * @param {string|Component|string[]} [first] 1453 * The event name, and array of event names, or another `Component`. 1454 * 1455 * @param {EventTarget~EventListener|string|string[]} [second] 1456 * The listener function, an event name, or an Array of events names. 1457 * 1458 * @param {EventTarget~EventListener} [third] 1459 * The event handler if `first` is a `Component` and `second` is an event name 1460 * or an Array of event names. 1461 * 1462 * @return {Component} 1463 * Returns itself; method can be chained. 1464 */ 1465 1466 1467 Component.prototype.off = function off(first, second, third) { 1468 if (!first || typeof first === 'string' || Array.isArray(first)) { 1469 Events.off(this.el_, first, second); 1470 } else { 1471 var target = first; 1472 var type = second; 1473 // Ensure there's at least a guid, even if the function hasn't been used 1474 var fn = Fn.bind(this, third); 1475 1476 // Remove the dispose listener on this component, 1477 // which was given the same guid as the event listener 1478 this.off('dispose', fn); 1479 1480 if (first.nodeName) { 1481 // Remove the listener 1482 Events.off(target, type, fn); 1483 // Remove the listener for cleaning the dispose listener 1484 Events.off(target, 'dispose', fn); 1485 } else {
1486 target.off(type, fn); 1487 target.off('dispose', fn); 1488 } 1489 } 1490 1491 return this; 1492 }; 1493 1494 /** 1495 * Add an event listener that gets triggered only once and then gets removed. 1496 * 1497 * @param {string|Component|string[]} [first] 1498 * The event name, and array of event names, or another `Component`. 1499 * 1500 * @param {EventTarget~EventListener|string|string[]} [second] 1501 * The listener function, an event name, or an Array of events names. 1502 * 1503 * @param {EventTarget~EventListener} [third] 1504 * The event handler if `first` is a `Component` and `second` is an event name 1505 * or an Array of event names. 1506 * 1507 * @return {Component} 1508 * Returns itself; method can be chained. 1509 */ 1510 1511 1512 Component.prototype.one = function one(first, second, third) { 1513 var _this3 = this, 1514 _arguments = arguments; 1515 1516 if (typeof first === 'string' || Array.isArray(first)) { 1517 Events.one(this.el_, first, Fn.bind(this, second)); 1518 } else { 1519 var target = first; 1520 var type = second; 1521 var fn = Fn.bind(this, third); 1522 1523 var newFunc = function newFunc() { 1524 _this3.off(target, type, newFunc); 1525 fn.apply(null, _arguments); 1526 }; 1527 1528 // Keep the same function ID so we can remove it later 1529 newFunc.guid = fn.guid; 1530 1531 this.on(target, type, newFunc); 1532 } 1533 1534 return this; 1535 }; 1536 1537 /** 1538 * Trigger an event on an element. 1539 * 1540 * @param {EventTarget~Event|Object|string} event 1541 * The event name, and Event, or an event-like object with a type attribute 1542 * set to the event name. 1543 * 1544 * @param {Object} [hash] 1545 * Data hash to pass along with the event 1546 * 1547 * @return {Component} 1548 * Returns itself; method can be chained. 1549 */ 1550 1551 1552 Component.prototype.trigger = function trigger(event, hash) { 1553 Events.trigger(this.el_, event, hash); 1554 return this; 1555 }; 1556 1557 /** 1558 * Bind a listener to the component's ready state. If the ready event has already 1559 * happened it will trigger the function immediately. 1560 * 1561 * @param {Component~ReadyCallback} fn 1562 * A function to call when ready is triggered. 1563 * 1564 * @param {boolean} [sync=false] 1565 * Execute the listener synchronously if `Component` is ready. 1566 * 1567 * @return {Component} 1568 * Returns itself; method can be chained. 1569 */ 1570 1571 1572 Component.prototype.ready = function ready(fn) { 1573 var sync = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; 1574 1575 if (fn) { 1576 if (this.isReady_) { 1577 if (sync) { 1578 fn.call(this); 1579 } else { 1580 // Call the function asynchronously by default for consistency 1581 this.setTimeout(fn, 1); 1582 } 1583 } else { 1584 this.readyQueue_ = this.readyQueue_ || []; 1585 this.readyQueue_.push(fn); 1586 } 1587 } 1588 return this; 1589 }; 1590 1591 /** 1592 * Trigger all the ready listeners for this `Component`. 1593 * 1594 * @fires Component#ready 1595 */ 1596 1597 1598 Component.prototype.triggerReady = function triggerReady() { 1599 this.isReady_ = true; 1600 1601 // Ensure ready is triggerd asynchronously 1602 this.setTimeout(function () { 1603 var readyQueue = this.readyQueue_; 1604 1605 // Reset Ready Queue 1606 this.readyQueue_ = []; 1607 1608 if (readyQueue && readyQueue.length > 0) { 1609 readyQueue.forEach(function (fn) { 1610 fn.call(this); 1611 }, this); 1612 } 1613 1614 // Allow for using event listeners also 1615 /** 1616 * Triggered when a `Component` is ready. 1617 * 1618 * @event Component#ready 1619 * @type {EventTarget~Event} 1620 */ 1621 this.trigger('ready'); 1622 }, 1); 1623 }; 1624 1625 /** 1626 * Find a single DOM element matching a `selector`. This can be within the `Component`s 1627 * `contentEl()` or another custom context. 1628 * 1629 * @param {string} selector 1630 * A valid CSS selector, which will be passed to `querySelector`. 1631 * 1632 * @param {Element|string} [context=this.contentEl()] 1633 * A DOM element within which to query. Can also be a selector string in 1634 * which case the first matching element will get used as context. If 1635 * missing `this.contentEl()` gets used. If `this.contentEl()` returns 1636 * nothing it falls back to `document`. 1637 * 1638 * @return {Element|null} 1639 * the dom element that was found, or null 1640 * 1641 * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors) 1642 */ 1643 1644 1645 Component.prototype.$ = function $(selector, context) { 1646 return Dom.$(selector, context || this.contentEl()); 1647 }; 1648 1649 /** 1650 * Finds all DOM element matching a `selector`. This can be within the `Component`s 1651 * `contentEl()` or another custom context. 1652 * 1653 * @param {string} selector 1654 * A valid CSS selector, which will be passed to `querySelectorAll`. 1655 * 1656 * @param {Element|string} [context=this.contentEl()] 1657 * A DOM element within which to query. Can also be a selector string in 1658 * which case the first matching element will get used as context. If 1659 * missing `this.contentEl()` gets used. If `this.contentEl()` returns 1660 * nothing it falls back to `document`. 1661 * 1662 * @return {NodeList} 1663 * a list of dom elements that were found 1664 * 1665 * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors) 1666 */ 1667 1668 1669 Component.prototype.$$ = function $$(selector, context) { 1670 return Dom.$$(selector, context || this.contentEl()); 1671 }; 1672 1673 /** 1674 * Check if a component's element has a CSS class name. 1675 * 1676 * @param {string} classToCheck 1677 * CSS class name to check. 1678 * 1679 * @return {boolean}
1680 * - True if the `Component` has the class. 1681 * - False if the `Component` does not have the class` 1682 */ 1683 1684 1685 Component.prototype.hasClass = function hasClass(classToCheck) { 1686 return Dom.hasElClass(this.el_, classToCheck); 1687 }; 1688 1689 /** 1690 * Add a CSS class name to the `Component`s element. 1691 * 1692 * @param {string} classToAdd 1693 * CSS class name to add 1694 * 1695 * @return {Component} 1696 * Returns itself; method can be chained. 1697 */ 1698 1699 1700 Component.prototype.addClass = function addClass(classToAdd) { 1701 Dom.addElClass(this.el_, classToAdd); 1702 return this; 1703 }; 1704 1705 /** 1706 * Remove a CSS class name from the `Component`s element. 1707 * 1708 * @param {string} classToRemove 1709 * CSS class name to remove 1710 * 1711 * @return {Component} 1712 * Returns itself; method can be chained. 1713 */ 1714 1715 1716 Component.prototype.removeClass = function removeClass(classToRemove) { 1717 Dom.removeElClass(this.el_, classToRemove); 1718 return this; 1719 }; 1720 1721 /** 1722 * Add or remove a CSS class name from the component's element. 1723 * - `classToToggle` gets added when {@link Component#hasClass} would return false. 1724 * - `classToToggle` gets removed when {@link Component#hasClass} would return true. 1725 * 1726 * @param {string} classToToggle 1727 * The class to add or remove based on (@link Component#hasClass} 1728 * 1729 * @param {boolean|Dom~predicate} [predicate] 1730 * An {@link Dom~predicate} function or a boolean 1731 * 1732 * @return {Component} 1733 * Returns itself; method can be chained. 1734 */ 1735 1736 1737 Component.prototype.toggleClass = function toggleClass(classToToggle, predicate) { 1738 Dom.toggleElClass(this.el_, classToToggle, predicate); 1739 return this; 1740 }; 1741 1742 /** 1743 * Show the `Component`s element if it is hidden by removing the 1744 * 'vjs-hidden' class name from it. 1745 * 1746 * @return {Component} 1747 * Returns itself; method can be chained. 1748 */ 1749 1750 1751 Component.prototype.show = function show() { 1752 this.removeClass('vjs-hidden'); 1753 return this; 1754 }; 1755 1756 /** 1757 * Hide the `Component`s element if it is currently showing by adding the 1758 * 'vjs-hidden` class name to it. 1759 * 1760 * @return {Component} 1761 * Returns itself; method can be chained. 1762 */ 1763 1764 1765 Component.prototype.hide = function hide() { 1766 this.addClass('vjs-hidden'); 1767 return this; 1768 }; 1769 1770 /** 1771 * Lock a `Component`s element in its visible state by adding the 'vjs-lock-showing' 1772 * class name to it. Used during fadeIn/fadeOut. 1773 * 1774 * @return {Component} 1775 * Returns itself; method can be chained. 1776 * 1777 * @private 1778 */ 1779 1780 1781 Component.prototype.lockShowing = function lockShowing() { 1782 this.addClass('vjs-lock-showing'); 1783 return this; 1784 }; 1785 1786 /** 1787 * Unlock a `Component`s element from its visible state by removing the 'vjs-lock-showing' 1788 * class name from it. Used during fadeIn/fadeOut. 1789 * 1790 * @return {Component} 1791 * Returns itself; method can be chained. 1792 * 1793 * @private 1794 */ 1795 1796 1797 Component.prototype.unlockShowing = function unlockShowing() { 1798 this.removeClass('vjs-lock-showing'); 1799 return this; 1800 }; 1801 1802 /** 1803 * Get the value of an attribute on the `Component`s element. 1804 * 1805 * @param {string} attribute 1806 * Name of the attribute to get the value from. 1807 * 1808 * @return {string|null} 1809 * - The value of the attribute that was asked for. 1810 * - Can be an empty string on some browsers if the attribute does not exist 1811 * or has no value 1812 * - Most browsers will return null if the attibute does not exist or has 1813 * no value. 1814 * 1815 * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute} 1816 */ 1817 1818 1819 Component.prototype.getAttribute = function getAttribute(attribute) { 1820 return Dom.getAttribute(this.el_, attribute); 1821 }; 1822 1823 /** 1824 * Set the value of an attribute on the `Component`'s element 1825 * 1826 * @param {string} attribute 1827 * Name of the attribute to set. 1828 * 1829 * @param {string} value 1830 * Value to set the attribute to. 1831 * 1832 * @return {Component} 1833 * Returns itself; method can be chained. 1834 * 1835 * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute} 1836 */ 1837 1838 1839 Component.prototype.setAttribute = function setAttribute(attribute, value) { 1840 Dom.setAttribute(this.el_, attribute, value); 1841 return this; 1842 }; 1843 1844 /** 1845 * Remove an attribute from the `Component`s element. 1846 * 1847 * @param {string} attribute 1848 * Name of the attribute to remove. 1849 * 1850 * @return {Component} 1851 * Returns itself; method can be chained. 1852 * 1853 * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttribute} 1854 */ 1855 1856 1857 Component.prototype.removeAttribute = function removeAttribute(attribute) { 1858 Dom.removeAttribute(this.el_, attribute); 1859 return this; 1860 }; 1861 1862 /** 1863 * Get or set the width of the component based upon the CSS styles. 1864 * See {@link Component#dimension} for more detailed information. 1865 * 1866 * @param {number|string} [num] 1867 * The width that you want to set postfixed with '%', 'px' or nothing. 1868 * 1869 * @param {boolean} [skipListeners] 1870 * Skip the resize event trigger 1871 * 1872 * @return {Component|number|string} 1873 * - The width when getting, zero if there is no width. Can be a string 1874 * postpixed with '%' or 'px'. 1875 * - Returns itself when setting; method can be chained. 1876 */ 1877 1878 1879 Component.prototype.width = function width(num, skipListeners) { 1880 return this.dimension('width', num, skipListeners); 1881 }; 1882 1883 /** 1884 * Get or set the height of the component based upon the CSS styles. 1885 * See {@link Component#dimension} for more detailed information. 1886 * 1887 * @param {number|string} [num] 1888 * The height that you want to set postfixed with '%', 'px' or nothing. 1889 * 1890 * @param {boolean} [skipListeners] 1891 * Skip the resize event trigger 1892 * 1893 * @return {Component|number|string} 1894 * - The width when getting, zero if there is no width. Can be a string 1895 * postpixed with '%' or 'px'. 1896 * - Returns itself when setting; method can be chained. 1897 */ 1898 1899 1900 Component.prototype.height = function height(num, skipListeners) { 1901 return this.dimension('height', num, skipListeners); 1902 }; 1903 1904 /** 1905 * Set both the width and height of the `Component` element at the same time. 1906 * 1907 * @param {number|string} width 1908 * Width to set the `Component`s element to. 1909 * 1910 * @param {number|string} height 1911 * Height to set the `Component`s element to. 1912 * 1913 * @return {Component} 1914 * Returns itself; method can be chained. 1915 */ 1916 1917 1918 Component.prototype.dimensions = function dimensions(width, height) { 1919 // Skip resize listeners on width for optimization 1920 return this.width(width, true).height(height); 1921 }; 1922 1923 /** 1924 * Get or set width or height of the `Component` element. This is the shared code 1925 * for the {@link Component#width} and {@link Component#height}. 1926 * 1927 * Things to know: 1928 * - If the width or height in an number this will return the number postfixed with 'px'. 1929 * - If the width/height is a percent this will return the percent postfixed with '%' 1930 * - Hidden elements have a width of 0 with `window.getComputedStyle`. This function 1931 * defaults to the `Component`s `style.width` and falls back to `window.getComputedStyle`. 1932 * See [this]{@link http://www.foliotek.com/devblog/getting-the-width-of-a-hidden-element-with-jquery-u
1932sing-width/} 1933 * for more information 1934 * - If you want the computed style of the component, use {@link Component#currentWidth} 1935 * and {@link {Component#currentHeight} 1936 * 1937 * @fires Component#resize 1938 * 1939 * @param {string} widthOrHeight 1940 8 'width' or 'height' 1941 * 1942 * @param {number|string} [num] 1943 8 New dimension 1944 * 1945 * @param {boolean} [skipListeners] 1946 * Skip resize event trigger 1947 * 1948 * @return {Component} 1949 * - the dimension when getting or 0 if unset 1950 * - Returns itself when setting; method can be chained. 1951 */ 1952 1953 1954 Component.prototype.dimension = function dimension(widthOrHeight, num, skipListeners) { 1955 if (num !== undefined) { 1956 // Set to zero if null or literally NaN (NaN !== NaN) 1957 if (num === null || num !== num) { 1958 num = 0; 1959 } 1960 1961 // Check if using css width/height (% or px) and adjust 1962 if (('' + num).indexOf('%') !== -1 || ('' + num).indexOf('px') !== -1) { 1963 this.el_.style[widthOrHeight] = num; 1964 } else if (num === 'auto') { 1965 this.el_.style[widthOrHeight] = ''; 1966 } else { 1967 this.el_.style[widthOrHeight] = num + 'px'; 1968 } 1969 1970 // skipListeners allows us to avoid triggering the resize event when setting both width and height 1971 if (!skipListeners) { 1972 /** 1973 * Triggered when a component is resized. 1974 * 1975 * @event Component#resize 1976 * @type {EventTarget~Event} 1977 */ 1978 this.trigger('resize'); 1979 } 1980 1981 // Return component 1982 return this; 1983 } 1984 1985 // Not setting a value, so getting it 1986 // Make sure element exists 1987 if (!this.el_) { 1988 return 0; 1989 } 1990 1991 // Get dimension value from style 1992 var val = this.el_.style[widthOrHeight]; 1993 var pxIndex = val.indexOf('px'); 1994 1995 if (pxIndex !== -1) { 1996 // Return the pixel value with no 'px' 1997 return parseInt(val.slice(0, pxIndex), 10); 1998 } 1999 2000 // No px so using % or no style was set, so falling back to offsetWidth/height 2001 // If component has display:none, offset will return 0 2002 // TODO: handle display:none and no dimension style using px 2003 return parseInt(this.el_['offset' + (0, _toTitleCase2['default'])(widthOrHeight)], 10); 2004 }; 2005 2006 /** 2007 * Get the width or the height of the `Component` elements computed style. Uses 2008 * `window.getComputedStyle`. 2009 * 2010 * @param {string} widthOrHeight 2011 * A string containing 'width' or 'height'. Whichever one you want to get. 2012 * 2013 * @return {number} 2014 * The dimension that gets asked for or 0 if nothing was set 2015 * for that dimension. 2016 */ 2017 2018 2019 Component.prototype.currentDimension = function currentDimension(widthOrHeight) { 2020 var computedWidthOrHeight = 0; 2021 2022 if (widthOrHeight !== 'width' && widthOrHeight !== 'height') { 2023 throw new Error('currentDimension only accepts width or height value'); 2024 } 2025 2026 if (typeof _window2['default'].getComputedStyle === 'function') { 2027 var computedStyle = _window2['default'].getComputedStyle(this.el_); 2028 2029 computedWidthOrHeight = computedStyle.getPropertyValue(widthOrHeight) || computedStyle[widthOrHeight]; 2030 } 2031 2032 // remove 'px' from variable and parse as integer 2033 computedWidthOrHeight = parseFloat(computedWidthOrHeight); 2034 2035 // if the computed value is still 0, it's possible that the browser is lying 2036 // and we want to check the offset values. 2037 // This code also runs on IE8 and wherever getComputedStyle doesn't exist. 2038 if (computedWidthOrHeight === 0) { 2039 var rule = 'offset' + (0, _toTitleCase2['default'])(widthOrHeight); 2040 2041 computedWidthOrHeight = this.el_[rule]; 2042 } 2043 2044 return computedWidthOrHeight; 2045 }; 2046 2047 /** 2048 * An object that contains width and height values of the `Component`s 2049 * computed style. Uses `window.getComputedStyle`. 2050 * 2051 * @typedef {Object} Component~DimensionObject 2052 * 2053 * @property {number} width 2054 * The width of the `Component`s computed style. 2055 * 2056 * @property {number} height 2057 * The height of the `Component`s computed style. 2058 */ 2059 2060 /** 2061 * Get an object that contains width and height values of the `Component`s 2062 * computed style. 2063 * 2064 * @return {Component~DimensionObject} 2065 * The dimensions of the components element 2066 */ 2067 2068 2069 Component.prototype.currentDimensions = function currentDimensions() { 2070 return { 2071 width: this.currentDimension('width'), 2072 height: this.currentDimension('height') 2073 }; 2074 }; 2075 2076 /** 2077 * Get the width of the `Component`s computed style. Uses `window.getComputedStyle`. 2078 * 2079 * @return {number} width 2080 * The width of the `Component`s computed style. 2081 */ 2082 2083 2084 Component.prototype.currentWidth = function currentWidth() { 2085 return this.currentDimension('width'); 2086 }; 2087 2088 /** 2089 * Get the height of the `Component`s computed style. Uses `window.getComputedStyle`. 2090 * 2091 * @return {number} height 2092 * The height of the `Component`s computed style. 2093 */ 2094 2095 2096 Component.prototype.currentHeight = function currentHeight() { 2097 return this.currentDimension('height'); 2098 }; 2099 2100 /** 2101 * Set the focus to this component 2102 */ 2103 2104
vendor: 9,394 bytes, lines 2105-2365
2105 Component.prototype.focus = function focus() { 2106 this.el_.focus(); 2107 }; 2108 2109 /** 2110 * Remove the focus from this component 2111 */ 2112 2113 2114 Component.prototype.blur = function blur() { 2115 this.el_.blur(); 2116 }; 2117 2118 /** 2119 * Emit a 'tap' events when touch event support gets detected. This gets used to 2120 * support toggling the controls through a tap on the video. They get enabled 2121 * because every sub-component would have extra overhead otherwise. 2122 * 2123 * @private 2124 * @fires Component#tap 2125 * @listens Component#touchstart 2126 * @listens Component#touchmove 2127 * @listens Component#touchleave 2128 * @listens Component#touchcancel 2129 * @listens Component#touchend 2130 */ 2131 2132 2133 Component.prototype.emitTapEvents = function emitTapEvents() { 2134 // Track the start time so we can determine how long the touch lasted 2135 var touchStart = 0; 2136 var firstTouch = null; 2137 2138 // Maximum movement allowed during a touch event to still be considered a tap 2139 // Other popular libs use anywhere from 2 (hammer.js) to 15, 2140 // so 10 seems like a nice, round number. 2141 var tapMovementThreshold = 10; 2142 2143 // The maximum length a touch can be while still being considered a tap 2144 var touchTimeThreshold = 200; 2145 2146 var couldBeTap = void 0; 2147 2148 this.on('touchstart', function (event) { 2149 // If more than one finger, don't consider treating this as a click 2150 if (event.touches.length === 1) { 2151 // Copy pageX/pageY from the object 2152 firstTouch = { 2153 pageX: event.touches[0].pageX, 2154 pageY: event.touches[0].pageY 2155 }; 2156 // Record start time so we can detect a tap vs. "touch and hold" 2157 touchStart = new Date().getTime(); 2158 // Reset couldBeTap tracking 2159 couldBeTap = true; 2160 } 2161 }); 2162 2163 this.on('touchmove', function (event) { 2164 // If more than one finger, don't consider treating this as a click 2165 if (event.touches.length > 1) { 2166 couldBeTap = false; 2167 } else if (firstTouch) { 2168 // Some devices will throw touchmoves for all but the slightest of taps. 2169 // So, if we moved only a small distance, this could still be a tap 2170 var xdiff = event.touches[0].pageX - firstTouch.pageX; 2171 var ydiff = event.touches[0].pageY - firstTouch.pageY; 2172 var touchDistance = Math.sqrt(xdiff * xdiff + ydiff * ydiff); 2173 2174 if (touchDistance > tapMovementThreshold) { 2175 couldBeTap = false; 2176 } 2177 } 2178 }); 2179 2180 var noTap = function noTap() { 2181 couldBeTap = false; 2182 }; 2183 2184 // TODO: Listen to the original target. http://youtu.be/DujfpXOKUp8?t=13m8s 2185 this.on('touchleave', noTap); 2186 this.on('touchcancel', noTap); 2187 2188 // When the touch ends, measure how long it took and trigger the appropriate 2189 // event 2190 this.on('touchend', function (event) { 2191 firstTouch = null; 2192 // Proceed only if the touchmove/leave/cancel event didn't happen 2193 if (couldBeTap === true) { 2194 // Measure how long the touch lasted 2195 var touchTime = new Date().getTime() - touchStart; 2196 2197 // Make sure the touch was less than the threshold to be considered a tap 2198 if (touchTime < touchTimeThreshold) { 2199 // Don't let browser turn this into a click 2200 event.preventDefault(); 2201 /** 2202 * Triggered when a `Component` is tapped. 2203 * 2204 * @event Component#tap 2205 * @type {EventTarget~Event} 2206 */ 2207 this.trigger('tap'); 2208 // It may be good to copy the touchend event object and change the 2209 // type to tap, if the other event properties aren't exact after 2210 // Events.fixEvent runs (e.g. event.target) 2211 } 2212 } 2213 }); 2214 }; 2215 2216 /** 2217 * This function reports user activity whenever touch events happen. This can get 2218 * turned off by any sub-components that wants touch events to act another way. 2219 * 2220 * Report user touch activity when touch events occur. User activity gets used to 2221 * determine when controls should show/hide. It is simple when it comes to mouse 2222 * events, because any mouse event should show the controls. So we capture mouse 2223 * events that bubble up to the player and report activity when that happens. 2224 * With touch events it isn't as easy as `touchstart` and `touchend` toggle player 2225 * controls. So touch events can't help us at the player level either. 2226 * 2227 * User activity gets checked asynchronously. So what could happen is a tap event 2228 * on the video turns the controls off. Then the `touchend` event bubbles up to 2229 * the player. Which, if it reported user activity, would turn the controls right 2230 * back on. We also don't want to completely block touch events from bubbling up. 2231 * Furthermore a `touchmove` event and anything other than a tap, should not turn 2232 * controls back on. 2233 * 2234 * @listens Component#touchstart 2235 * @listens Component#touchmove 2236 * @listens Component#touchend 2237 * @listens Component#touchcancel 2238 */ 2239 2240 2241 Component.prototype.enableTouchActivity = function enableTouchActivity() { 2242 // Don't continue if the root player doesn't support reporting user activity 2243 if (!this.player() || !this.player().reportUserActivity) { 2244 return; 2245 } 2246 2247 // listener for reporting that the user is active 2248 var report = Fn.bind(this.player(), this.player().reportUserActivity); 2249 2250 var touchHolding = void 0; 2251 2252 this.on('touchstart', function () { 2253 report(); 2254 // For as long as the they are touching the device or have their mouse down, 2255 // we consider them active even if they're not moving their finger or mouse. 2256 // So we want to continue to update that they are active 2257 this.clearInterval(touchHolding); 2258 // report at the same interval as activityCheck 2259 touchHolding = this.setInterval(report, 250); 2260 }); 2261 2262 var touchEnd = function touchEnd(event) { 2263 report(); 2264 // stop the interval that maintains activity if the touch is holding 2265 this.clearInterval(touchHolding); 2266 }; 2267 2268 this.on('touchmove', report); 2269 this.on('touchend', touchEnd); 2270 this.on('touchcancel', touchEnd); 2271 }; 2272 2273 /** 2274 * A callback that has no parameters and is bound into `Component`s context. 2275 * 2276 * @callback Component~GenericCallback 2277 * @this Component 2278 */ 2279 2280 /** 2281 * Creates a function that runs after an `x` millisecond timeout. This function is a 2282 * wrapper around `window.setTimeout`. There are a few reasons to use this one 2283 * instead though: 2284 * 1. It gets cleared via {@link Component#clearTimeout} when 2285 * {@link Component#dispose} gets called. 2286 * 2. The function callback will gets turned into a {@link Component~GenericCallback} 2287 * 2288 * > Note: You can use `window.clearTimeout` on the id returned by this function. This 2289 * will cause its dispose listener not to get cleaned up! Please use 2290 * {@link Component#clearTimeout} or {@link Component#dispose}. 2291 * 2292 * @param {Component~GenericCallback} fn 2293 * The function that will be run after `timeout`. 2294 * 2295 * @param {number} timeout 2296 * Timeout in milliseconds to delay before executing the specified function. 2297 * 2298 * @return {number} 2299 * Returns a timeout ID that gets used to identify the timeout. It can also 2300 * get used in {@link Component#clearTimeout} to clear the timeout that 2301 * was set. 2302 * 2303 * @listens Component#dispose 2304 * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout} 2305 */ 2306 2307 2308 Component.prototype.setTimeout = function setTimeout(fn, timeout) { 2309 fn = Fn.bind(this, fn); 2310 2311 var timeoutId = _window2['default'].setTimeout(fn, timeout); 2312 var disposeFn = function disposeFn() { 2313 this.clearTimeout(timeoutId); 2314 }; 2315 2316 disposeFn.guid = 'vjs-timeout-' + timeoutId; 2317 2318 this.on('dispose', disposeFn); 2319 2320 return timeoutId; 2321 }; 2322 2323 /** 2324 * Clears a timeout that gets created via `window.setTimeout` or 2325 * {@link Component#setTimeout}. If you set a timeout via {@link Component#setTimeout} 2326 * use this function instead of `window.clearTimout`. If you don't your dispose 2327 * listener will not get cleaned up until {@link Component#dispose}! 2328 * 2329 * @param {number} timeoutId 2330 * The id of the timeout to clear. The return value of 2331 * {@link Component#setTimeout} or `window.setTimeout`. 2332 * 2333 * @return {number} 2334 * Returns the timeout id that was cleared. 2335 * 2336 * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearTimeout} 2337 */ 2338 2339 2340 Component.prototype.clearTimeout = function clearTimeout(timeoutId) { 2341 _window2['default'].clearTimeout(timeoutId); 2342 2343 var disposeFn = function disposeFn() {}; 2344 2345 disposeFn.guid = 'vjs-timeout-' + timeoutId; 2346 2347 this.off('dispose', disposeFn); 2348 2349 return timeoutId; 2350 }; 2351 2352 /** 2353 * Creates a function that gets run every `x` milliseconds. This function is a wrapper 2354 * around `window.setInterval`. There are a few reasons to use this one instead though. 2355 * 1. It gets cleared via {@link Component#clearInterval} when 2356 * {@link Component#dispose} gets called. 2357 * 2. The function callback will be a {@link Component~GenericCallback} 2358 * 2359 * @param {Component~GenericCallback} fn 2360 * The function to run every `x` seconds. 2361 * 2362 * @param {number} interval 2363 * Execute the specified function every `x` milliseconds. 2364 * 2365 * @return {number}
2366 * Returns an id that can be used to identify the interval. It can also be be used in 2367 * {@link Component#clearInterval} to clear the interval. 2368 * 2369 * @listens Component#dispose 2370 * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval} 2371 */ 2372 2373 2374 Component.prototype.setInterval = function setInterval(fn, interval) { 2375 fn = Fn.bind(this, fn); 2376 2377 var intervalId = _window2['default'].setInterval(fn, interval); 2378 2379 var disposeFn = function disposeFn() { 2380 this.clearInterval(intervalId); 2381 }; 2382 2383 disposeFn.guid = 'vjs-interval-' + intervalId; 2384 2385 this.on('dispose', disposeFn); 2386 2387 return intervalId; 2388 }; 2389 2390 /** 2391 * Clears an interval that gets created via `window.setInterval` or 2392 * {@link Component#setInterval}. If you set an inteval via {@link Component#setInterval} 2393 * use this function instead of `window.clearInterval`. If you don't your dispose 2394 * listener will not get cleaned up until {@link Component#dispose}! 2395 * 2396 * @param {number} intervalId 2397 * The id of the interval to clear. The return value of 2398 * {@link Component#setInterval} or `window.setInterval`. 2399 * 2400 * @return {number} 2401 * Returns the interval id that was cleared. 2402 * 2403 * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearInterval} 2404 */ 2405 2406 2407 Component.prototype.clearInterval = function clearInterval(intervalId) { 2408 _window2['default'].clearInterval(intervalId); 2409 2410 var disposeFn = function disposeFn() {}; 2411 2412 disposeFn.guid = 'vjs-interval-' + intervalId; 2413 2414 this.off('dispose', disposeFn); 2415 2416 return intervalId; 2417 }; 2418 2419 /** 2420 * Register a `Component` with `videojs` given the name and the component. 2421 * 2422 * > NOTE: {@link Tech}s should not be registered as a `Component`. {@link Tech}s 2423 * should be registered using {@link Tech.registerTech} or 2424 * {@link videojs:videojs.registerTech}. 2425 * 2426 * > NOTE: This function can also be seen on videojs as 2427 * {@link videojs:videojs.registerComponent}. 2428 * 2429 * @param {string} name 2430 * The name of the `Component` to register. 2431 * 2432 * @param {Component} comp 2433 * The `Component` class to register. 2434 * 2435 * @return {Component} 2436 * The `Component` that was registered. 2437 */ 2438 2439 2440 Component.registerComponent = function registerComponent(name, comp) { 2441 if (!name) { 2442 return; 2443 } 2444 2445 name = (0, _toTitleCase2['default'])(name); 2446 2447 if (!Component.components_) { 2448 Component.components_ = {}; 2449 } 2450 2451 if (name === 'Player' && Component.components_[name]) { 2452 var Player = Component.components_[name]; 2453 2454 // If we have players that were disposed, then their name will still be 2455 // in Players.players. So, we must loop through and verify that the value 2456 // for each item is not null. This allows registration of the Player component 2457 // after all players have been disposed or before any were created. 2458 if (Player.players && Object.keys(Player.players).length > 0 && Object.keys(Player.players).map(function (playerName) { 2459 return Player.players[playerName]; 2460 }).every(Boolean)) { 2461 throw new Error('Can not register Player component after player has been created'); 2462 } 2463 } 2464 2465 Component.components_[name] = comp; 2466 2467 return comp; 2468 }; 2469 2470 /** 2471 * Get a `Component` based on the name it was registered with. 2472 * 2473 * @param {string} name 2474 * The Name of the component to get. 2475 * 2476 * @return {Component} 2477 * The `Component` that got registered under the given name. 2478 * 2479 * @deprecated In `videojs` 6 this will not return `Component`s that were not 2480 * registered using {@link Component.registerComponent}. Currently we 2481 * check the global `videojs` object for a `Component` name and 2482 * return that if it exists. 2483 */ 2484 2485 2486 Component.getComponent = function getComponent(name) { 2487 if (!name) { 2488 return; 2489 } 2490 2491 name = (0, _toTitleCase2['default'])(name); 2492 2493 if (Component.components_ && Component.components_[name]) { 2494 return Component.components_[name]; 2495 } 2496 2497 if (_window2['default'] && _window2['default'].videojs && _window2['default'].videojs[name]) { 2498 _log2['default'].warn('The ' + name + ' component was added to the videojs object when it should be registered using videojs.registerComponent(name, component)'); 2499 2500 return _window2['default'].videojs[name]; 2501 } 2502 }; 2503 2504 /** 2505 * Sets up the constructor using the supplied init method or uses the init of the 2506 * parent object. 2507 * 2508 * @param {Object} [props={}] 2509 * An object of properties. 2510 * 2511 * @return {Object} 2512 * the extended object. 2513 * 2514 * @deprecated since version 5 2515 */ 2516 2517 2518 Component.extend = function extend(props) { 2519 props = props || {}; 2520 2521 _log2['default'].warn('Component.extend({}) has been deprecated, ' + ' use videojs.extend(Component, {}) instead'); 2522 2523 // Set up the constructor using the supplied init method 2524 // or using the init of the parent object 2525 // Make sure to check the unobfuscated version for external libs 2526 var init = props.init || props.init || this.prototype.init || this.prototype.init || function () {}; 2527 // In Resig's simple class inheritance (previously used) the constructor 2528 // is a function that calls `this.init.apply(arguments)` 2529 // However that would prevent us from using `ParentObject.call(this);` 2530 // in a Child constructor because the `this` in `this.init` 2531 // would still refer to the Child and cause an infinite loop. 2532 // We would instead have to do
2533 // `ParentObject.prototype.init.apply(this, arguments);` 2534 // Bleh. We're not creating a _super() function, so it's good to keep 2535 // the parent constructor reference simple. 2536 var subObj = function subObj() { 2537 init.apply(this, arguments); 2538 }; 2539 2540 // Inherit from this object's prototype 2541 subObj.prototype = Object.create(this.prototype); 2542 // Reset the constructor property for subObj otherwise 2543 // instances of subObj would have the constructor of the parent Object 2544 subObj.prototype.constructor = subObj; 2545 2546 // Make the class extendable 2547 subObj.extend = Component.extend; 2548 2549 // Extend subObj's prototype with functions and other properties from props 2550 for (var name in props) { 2551 if (props.hasOwnProperty(name)) { 2552 subObj.prototype[name] = props[name]; 2553 } 2554 } 2555 2556 return subObj; 2557 }; 2558 2559 return Component; 2560}(); 2561 2562Component.registerComponent('Component', Component); 2563exports['default'] = Component; 2564 2565},{"81":81,"82":82,"83":83,"85":85,"86":86,"87":87,"91":91,"95":95}],6:[function(_dereq_,module,exports){ 2566'use strict'; 2567 2568exports.__esModule = true; 2569 2570var _trackButton = _dereq_(36); 2571 2572var _trackButton2 = _interopRequireDefault(_trackButton); 2573 2574var _component = _dereq_(5); 2575 2576var _component2 = _interopRequireDefault(_component); 2577 2578var _audioTrackMenuItem = _dereq_(7); 2579 2580var _audioTrackMenuItem2 = _interopRequireDefault(_audioTrackMenuItem); 2581 2582function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 2583 2584function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 2585 2586function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 2587 2588function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 2589 * @file audio-track-button.js 2590 */ 2591 2592 2593/** 2594 * The base class for buttons that toggle specific {@link AudioTrack} types. 2595 * 2596 * @extends TrackButton 2597 */ 2598var AudioTrackButton = function (_TrackButton) { 2599 _inherits(AudioTrackButton, _TrackButton); 2600 2601 /** 2602 * Creates an instance of this class. 2603 * 2604 * @param {Player} player 2605 * The `Player` that this class should be attached to. 2606 * 2607 * @param {Object} [options={}] 2608 * The key/value store of player options. 2609 */ 2610 function AudioTrackButton(player) { 2611 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 2612 2613 _classCallCheck(this, AudioTrackButton); 2614 2615 options.tracks = player.audioTracks && player.audioTracks(); 2616 2617 var _this = _possibleConstructorReturn(this, _TrackButton.call(this, player, options)); 2618 2619 _this.el_.setAttribute('aria-label', 'Audio Menu'); 2620 return _this; 2621 } 2622 2623 /** 2624 * Builds the default DOM `className`. 2625 * 2626 * @return {string} 2627 * The DOM `className` for this object. 2628 */ 2629 2630 2631 AudioTrackButton.prototype.buildCSSClass = function buildCSSClass() { 2632 return 'vjs-audio-button ' + _TrackButton.prototype.buildCSSClass.call(this); 2633 }; 2634 2635 /** 2636 * Create a menu item for each audio track 2637 * 2638 * @param {AudioTrackMenuItem[]} [items=[]] 2639 * An array of existing menu items to use. 2640 * 2641 * @return {AudioTrackMenuItem[]} 2642 * An array of menu items 2643 */ 2644 2645 2646 AudioTrackButton.prototype.createItems = function createItems() { 2647 var items = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; 2648 2649 // if there's only one audio track, there no point in showing it 2650 this.hideThreshold_ = 1; 2651 2652 var tracks = this.player_.audioTracks && this.player_.audioTracks(); 2653 2654 if (!tracks) { 2655 return items; 2656 } 2657 2658 for (var i = 0; i < tracks.length; i++) { 2659 var track = tracks[i]; 2660 2661 items.push(new _audioTrackMenuItem2['default'](this.player_, { 2662 track: track, 2663 // MenuItem is selectable 2664 selectable: true 2665 })); 2666 } 2667 2668 return items; 2669 }; 2670 2671 return AudioTrackButton; 2672}(_trackButton2['default']); 2673 2674/** 2675 * The text that should display over the `AudioTrackButton`s controls. Added for localization. 2676 * 2677 * @type {string} 2678 * @private 2679 */ 2680 2681 2682AudioTrackButton.prototype.controlText_ = 'Audio Track'; 2683_component2['default'].registerComponent('AudioTrackButton', AudioTrackButton); 2684exports['default'] = AudioTrackButton; 2685 2686},{"36":36,"5":5,"7":7}],7:[function(_dereq_,module,exports){ 2687'use strict'; 2688 2689exports.__esModule = true; 2690 2691var _menuItem = _dereq_(48); 2692 2693var _menuItem2 = _interopRequireDefault(_menuItem); 2694 2695var _component = _dereq_(5); 2696 2697var _component2 = _interopRequireDefault(_component); 2698 2699var _fn = _dereq_(83); 2700 2701var Fn = _interopRequireWildcard(_fn); 2702 2703function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 2704 2705function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 2706 2707function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 2708 2709function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 2710 2711function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
2711onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 2712 * @file audio-track-menu-item.js 2713 */ 2714 2715 2716/** 2717 * An {@link AudioTrack} {@link MenuItem} 2718 * 2719 * @extends MenuItem 2720 */ 2721var AudioTrackMenuItem = function (_MenuItem) { 2722 _inherits(AudioTrackMenuItem, _MenuItem); 2723 2724 /** 2725 * Creates an instance of this class. 2726 * 2727 * @param {Player} player 2728 * The `Player` that this class should be attached to. 2729 * 2730 * @param {Object} [options] 2731 * The key/value store of player options. 2732 */ 2733 function AudioTrackMenuItem(player, options) { 2734 _classCallCheck(this, AudioTrackMenuItem); 2735 2736 var track = options.track; 2737 var tracks = player.audioTracks(); 2738 2739 // Modify options for parent MenuItem class's init. 2740 options.label = track.label || track.language || 'Unknown'; 2741 options.selected = track.enabled; 2742 2743 var _this = _possibleConstructorReturn(this, _MenuItem.call(this, player, options)); 2744 2745 _this.track = track; 2746 2747 if (tracks) { 2748 var changeHandler = Fn.bind(_this, _this.handleTracksChange); 2749 2750 tracks.addEventListener('change', changeHandler); 2751 _this.on('dispose', function () { 2752 tracks.removeEventListener('change', changeHandler); 2753 }); 2754 } 2755 return _this; 2756 } 2757 2758 /** 2759 * This gets called when an `AudioTrackMenuItem is "clicked". See {@link ClickableComponent} 2760 * for more detailed information on what a click can be. 2761 * 2762 * @param {EventTarget~Event} [event] 2763 * The `keydown`, `tap`, or `click` event that caused this function to be 2764 * called. 2765 * 2766 * @listens tap 2767 * @listens click 2768 */ 2769 2770 2771 AudioTrackMenuItem.prototype.handleClick = function handleClick(event) { 2772 var tracks = this.player_.audioTracks(); 2773 2774 _MenuItem.prototype.handleClick.call(this, event); 2775 2776 if (!tracks) { 2777 return; 2778 } 2779 2780 for (var i = 0; i < tracks.length; i++) { 2781 var track = tracks[i]; 2782 2783 track.enabled = track === this.track; 2784 } 2785 }; 2786 2787 /** 2788 * Handle any {@link AudioTrack} change. 2789 * 2790 * @param {EventTarget~Event} [event] 2791 * The {@link AudioTrackList#change} event that caused this to run. 2792 * 2793 * @listens AudioTrackList#change 2794 */ 2795 2796 2797 AudioTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) { 2798 this.selected(this.track.enabled); 2799 }; 2800 2801 return AudioTrackMenuItem; 2802}(_menuItem2['default']); 2803 2804_component2['default'].registerComponent('AudioTrackMenuItem', AudioTrackMenuItem); 2805exports['default'] = AudioTrackMenuItem; 2806 2807},{"48":48,"5":5,"83":83}],8:[function(_dereq_,module,exports){ 2808'use strict'; 2809 2810exports.__esModule = true; 2811 2812var _component = _dereq_(5); 2813 2814var _component2 = _interopRequireDefault(_component); 2815 2816_dereq_(12); 2817 2818_dereq_(32); 2819 2820_dereq_(33); 2821 2822_dereq_(35); 2823 2824_dereq_(34); 2825 2826_dereq_(10); 2827 2828_dereq_(18); 2829 2830_dereq_(9); 2831 2832_dereq_(38); 2833 2834_dereq_(40); 2835 2836_dereq_(11); 2837 2838_dereq_(25); 2839 2840_dereq_(27); 2841 2842_dereq_(29); 2843 2844_dereq_(24); 2845 2846_dereq_(6); 2847 2848_dereq_(13); 2849 2850_dereq_(21); 2851 2852function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 2853 2854function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 2855 2856function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 2857 2858function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
2858onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 2859 * @file control-bar.js 2860 */ 2861 2862 2863// Required children 2864 2865 2866/** 2867 * Container of main controls. 2868 * 2869 * @extends Component 2870 */ 2871var ControlBar = function (_Component) { 2872 _inherits(ControlBar, _Component); 2873 2874 function ControlBar() { 2875 _classCallCheck(this, ControlBar); 2876 2877 return _possibleConstructorReturn(this, _Component.apply(this, arguments)); 2878 } 2879 2880 /** 2881 * Create the `Component`'s DOM element 2882 * 2883 * @return {Element} 2884 * The element that was created. 2885 */ 2886 ControlBar.prototype.createEl = function createEl() { 2887 return _Component.prototype.createEl.call(this, 'div', { 2888 className: 'vjs-control-bar', 2889 dir: 'ltr' 2890 }, { 2891 // The control bar is a group, so it can contain menuitems 2892 role: 'group' 2893 }); 2894 }; 2895 2896 return ControlBar; 2897}(_component2['default']); 2898 2899/** 2900 * Default options for `ControlBar` 2901 * 2902 * @type {Object} 2903 * @private 2904 */ 2905 2906 2907ControlBar.prototype.options_ = { 2908 children: ['playToggle', 'volumeMenuButton', 'currentTimeDisplay', 'timeDivider', 'durationDisplay', 'progressControl', 'liveDisplay', 'remainingTimeDisplay', 'customControlSpacer', 'playbackRateMenuButton', 'chaptersButton', 'descriptionsButton', 'subtitlesButton', 'captionsButton', 'audioTrackButton', 'fullscreenToggle'] 2909}; 2910 2911_component2['default'].registerComponent('ControlBar', ControlBar); 2912exports['default'] = ControlBar; 2913 2914},{"10":10,"11":11,"12":12,"13":13,"18":18,"21":21,"24":24,"25":25,"27":27,"29":29,"32":32,"33":33,"34":34,"35":35,"38":38,"40":40,"5":5,"6":6,"9":9}],9:[function(_dereq_,module,exports){ 2915'use strict'; 2916 2917exports.__esModule = true; 2918 2919var _button = _dereq_(2); 2920 2921var _button2 = _interopRequireDefault(_button); 2922 2923var _component = _dereq_(5); 2924 2925var _component2 = _interopRequireDefault(_component); 2926 2927function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 2928 2929function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 2930 2931function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 2932 2933function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 2934 * @file fullscreen-toggle.js 2935 */ 2936 2937 2938/** 2939 * Toggle fullscreen video 2940 * 2941 * @extends Button 2942 */ 2943var FullscreenToggle = function (_Button) { 2944 _inherits(FullscreenToggle, _Button); 2945 2946 /** 2947 * Creates an instance of this class. 2948 * 2949 * @param {Player} player 2950 * The `Player` that this class should be attached to. 2951 * 2952 * @param {Object} [options] 2953 * The key/value store of player options. 2954 */ 2955 function FullscreenToggle(player, options) { 2956 _classCallCheck(this, FullscreenToggle); 2957 2958 var _this = _possibleConstructorReturn(this, _Button.call(this, player, options)); 2959 2960 _this.on(player, 'fullscreenchange', _this.handleFullscreenChange); 2961 return _this; 2962 } 2963 2964 /** 2965 * Builds the default DOM `className`. 2966 * 2967 * @return {string} 2968 * The DOM `className` for this object. 2969 */ 2970 2971 2972 FullscreenToggle.prototype.buildCSSClass = function buildCSSClass() { 2973 return 'vjs-fullscreen-control ' + _Button.prototype.buildCSSClass.call(this); 2974 }; 2975 2976 /** 2977 * Handles fullscreenchange on the player and change control text accordingly. 2978 * 2979 * @param {EventTarget~Event} [event] 2980 * The {@link Player#fullscreenchange} event that caused this function to be 2981 * called. 2982 * 2983 * @listens Player#fullscreenchange 2984 */ 2985 2986 2987 FullscreenToggle.prototype.handleFullscreenChange = function handleFullscreenChange(event) { 2988 if (this.player_.isFullscreen()) { 2989 this.controlText('Non-Fullscreen'); 2990 } else { 2991 this.controlText('Fullscreen'); 2992 } 2993 }; 2994 2995 /** 2996 * This gets called when an `FullscreenToggle` is "clicked". See 2997 * {@link ClickableComponent} for more detailed information on what a click can be. 2998 * 2999 * @param {EventTarget~Event} [event]
3000 * The `keydown`, `tap`, or `click` event that caused this function to be 3001 * called. 3002 * 3003 * @listens tap 3004 * @listens click 3005 */ 3006 3007 3008 FullscreenToggle.prototype.handleClick = function handleClick(event) { 3009 if (!this.player_.isFullscreen()) { 3010 this.player_.requestFullscreen(); 3011 } else { 3012 this.player_.exitFullscreen(); 3013 } 3014 }; 3015 3016 return FullscreenToggle; 3017}(_button2['default']); 3018 3019/** 3020 * The text that should display over the `FullscreenToggle`s controls. Added for localization. 3021 * 3022 * @type {string} 3023 * @private 3024 */ 3025 3026 3027FullscreenToggle.prototype.controlText_ = 'Fullscreen'; 3028 3029_component2['default'].registerComponent('FullscreenToggle', FullscreenToggle); 3030exports['default'] = FullscreenToggle; 3031 3032},{"2":2,"5":5}],10:[function(_dereq_,module,exports){ 3033'use strict'; 3034 3035exports.__esModule = true; 3036 3037var _component = _dereq_(5); 3038 3039var _component2 = _interopRequireDefault(_component); 3040 3041var _dom = _dereq_(81); 3042 3043var Dom = _interopRequireWildcard(_dom); 3044 3045function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 3046 3047function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 3048 3049function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 3050 3051function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 3052 3053function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 3054 * @file live-display.js 3055 */ 3056 3057 3058// TODO - Future make it click to snap to live 3059 3060/** 3061 * Displays the live indicator when duration is Infinity. 3062 * 3063 * @extends Component 3064 */ 3065var LiveDisplay = function (_Component) { 3066 _inherits(LiveDisplay, _Component); 3067 3068 /** 3069 * Creates an instance of this class. 3070 * 3071 * @param {Player} player 3072 * The `Player` that this class should be attached to. 3073 * 3074 * @param {Object} [options] 3075 * The key/value store of player options. 3076 */ 3077 function LiveDisplay(player, options) { 3078 _classCallCheck(this, LiveDisplay); 3079 3080 var _this = _possibleConstructorReturn(this, _Component.call(this, player, options)); 3081 3082 _this.updateShowing(); 3083 _this.on(_this.player(), 'durationchange', _this.updateShowing); 3084 return _this; 3085 } 3086 3087 /** 3088 * Create the `Component`'s DOM element 3089 * 3090 * @return {Element} 3091 * The element that was created. 3092 */ 3093 3094 3095 LiveDisplay.prototype.createEl = function createEl() { 3096 var el = _Component.prototype.createEl.call(this, 'div', { 3097 className: 'vjs-live-control vjs-control' 3098 }); 3099 3100 this.contentEl_ = Dom.createEl('div', { 3101 className: 'vjs-live-display', 3102 innerHTML: '<span class="vjs-control-text">' + this.localize('Stream Type') + '</span>' + this.localize('LIVE') 3103 }, {
3104 'aria-live': 'off' 3105 }); 3106 3107 el.appendChild(this.contentEl_); 3108 return el; 3109 }; 3110 3111 /** 3112 * Check the duration to see if the LiveDisplay should be showing or not. Then show/hide 3113 * it accordingly 3114 * 3115 * @param {EventTarget~Event} [event] 3116 * The {@link Player#durationchange} event that caused this function to run. 3117 * 3118 * @listens Player#durationchange 3119 */ 3120 3121 3122 LiveDisplay.prototype.updateShowing = function updateShowing(event) { 3123 if (this.player().duration() === Infinity) { 3124 this.show(); 3125 } else { 3126 this.hide(); 3127 } 3128 }; 3129 3130 return LiveDisplay; 3131}(_component2['default']); 3132 3133_component2['default'].registerComponent('LiveDisplay', LiveDisplay); 3134exports['default'] = LiveDisplay; 3135 3136},{"5":5,"81":81}],11:[function(_dereq_,module,exports){ 3137'use strict'; 3138 3139exports.__esModule = true; 3140 3141var _button = _dereq_(2); 3142 3143var _button2 = _interopRequireDefault(_button); 3144 3145var _component = _dereq_(5); 3146 3147var _component2 = _interopRequireDefault(_component); 3148 3149var _dom = _dereq_(81); 3150 3151var Dom = _interopRequireWildcard(_dom); 3152 3153function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 3154 3155function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 3156 3157function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 3158 3159function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 3160 3161function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 3162 * @file mute-toggle.js 3163 */ 3164 3165 3166/** 3167 * A button component for muting the audio. 3168 * 3169 * @extends Button 3170 */ 3171var MuteToggle = function (_Button) { 3172 _inherits(MuteToggle, _Button); 3173 3174 /** 3175 * Creates an instance of this class. 3176 * 3177 * @param {Player} player 3178 * The `Player` that this class should be attached to. 3179 * 3180 * @param {Object} [options] 3181 * The key/value store of player options. 3182 */ 3183 function MuteToggle(player, options) { 3184 _classCallCheck(this, MuteToggle); 3185 3186 var _this = _possibleConstructorReturn(this, _Button.call(this, player, options)); 3187 3188 _this.on(player, 'volumechange', _this.update); 3189 3190 // hide mute toggle if the current tech doesn't support volume control 3191 if (player.tech_ && player.tech_.featuresVolumeControl === false) { 3192 _this.addClass('vjs-hidden'); 3193 } 3194 3195 _this.on(player, 'loadstart', function () { 3196 // We need to update the button to account for a default muted state. 3197 this.update(); 3198 3199 if (player.tech_.featuresVolumeControl === false) { 3200 this.addClass('vjs-hidden'); 3201 } else { 3202 this.removeClass('vjs-hidden'); 3203 } 3204 }); 3205 return _this; 3206 } 3207 3208 /** 3209 * Builds the default DOM `className`. 3210 * 3211 * @return {string} 3212 * The DOM `className` for this object. 3213 */ 3214 3215 3216 MuteToggle.prototype.buildCSSClass = function buildCSSClass() { 3217 return 'vjs-mute-control ' + _Button.prototype.buildCSSClass.call(this); 3218 }; 3219 3220 /** 3221 * This gets called when an `MuteToggle` is "clicked". See 3222 * {@link ClickableComponent} for more detailed information on what a click can be. 3223 * 3224 * @param {EventTarget~Event} [event]
3225 * The `keydown`, `tap`, or `click` event that caused this function to be 3226 * called. 3227 * 3228 * @listens tap 3229 * @listens click 3230 */ 3231 3232 3233 MuteToggle.prototype.handleClick = function handleClick(event) { 3234 this.player_.muted(this.player_.muted() ? false : true); 3235 }; 3236 3237 /** 3238 * Update the state of volume. 3239 * 3240 * @param {EventTarget~Event} [event] 3241 * The {@link Player#loadstart} event if this function was called through an 3242 * event. 3243 * 3244 * @listens Player#loadstart 3245 */ 3246 3247 3248 MuteToggle.prototype.update = function update(event) { 3249 var vol = this.player_.volume(); 3250 var level = 3; 3251 3252 if (this.player_.muted()) { 3253 level = 0; 3254 } else if (vol < 0.33) { 3255 level = 1; 3256 } else if (vol < 0.67) { 3257 level = 2; 3258 } 3259 3260 // Don't rewrite the button text if the actual text doesn't change. 3261 // This causes unnecessary and confusing information for screen reader users. 3262 // This check is needed because this function gets called every time the volume level is changed. 3263 var toMute = this.player_.muted() ? 'Unmute' : 'Mute'; 3264 3265 if (this.controlText() !== toMute) { 3266 this.controlText(toMute); 3267 } 3268 3269 // TODO improve muted icon classes 3270 for (var i = 0; i < 4; i++) { 3271 Dom.removeElClass(this.el_, 'vjs-vol-' + i); 3272 } 3273 Dom.addElClass(this.el_, 'vjs-vol-' + level); 3274 }; 3275 3276 return MuteToggle; 3277}(_button2['default']); 3278 3279/** 3280 * The text that should display over the `MuteToggle`s controls. Added for localization. 3281 * 3282 * @type {string} 3283 * @private 3284 */ 3285 3286 3287MuteToggle.prototype.controlText_ = 'Mute'; 3288 3289_component2['default'].registerComponent('MuteToggle', MuteToggle); 3290exports['default'] = MuteToggle; 3291 3292},{"2":2,"5":5,"81":81}],12:[function(_dereq_,module,exports){ 3293'use strict'; 3294 3295exports.__esModule = true; 3296 3297var _button = _dereq_(2); 3298 3299var _button2 = _interopRequireDefault(_button); 3300 3301var _component = _dereq_(5); 3302 3303var _component2 = _interopRequireDefault(_component); 3304 3305function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 3306 3307function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 3308 3309function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 3310 3311function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 3312 * @file play-toggle.js 3313 */ 3314 3315 3316/** 3317 * Button to toggle between play and pause. 3318 * 3319 * @extends Button 3320 */ 3321var PlayToggle = function (_Button) { 3322 _inherits(PlayToggle, _Button); 3323 3324 /** 3325 * Creates an instance of this class. 3326 * 3327 * @param {Player} player 3328 * The `Player` that this class should be attached to. 3329 * 3330 * @param {Object} [options] 3331 * The key/value store of player options. 3332 */ 3333 function PlayToggle(player, options) { 3334 _classCallCheck(this, PlayToggle); 3335 3336 var _this = _possibleConstructorReturn(this, _Button.call(this, player, options)); 3337 3338 _this.on(player, 'play', _this.handlePlay); 3339 _this.on(player, 'pause', _this.handlePause); 3340 return _this; 3341 } 3342 3343 /** 3344 * Builds the default DOM `className`. 3345 * 3346 * @return {string} 3347 * The DOM `className` for this object. 3348 */ 3349 3350 3351 PlayToggle.prototype.buildCSSClass = function buildCSSClass() { 3352 return 'vjs-play-control ' + _Button.prototype.buildCSSClass.call(this); 3353 }; 3354 3355 /** 3356 * This gets called when an `PlayToggle` is "clicked". See 3357 * {@link ClickableComponent} for more detailed information on what a click can be. 3358 * 3359 * @param {EventTarget~Event} [event]
3360 * The `keydown`, `tap`, or `click` event that caused this function to be 3361 * called. 3362 * 3363 * @listens tap 3364 * @listens click 3365 */ 3366 3367 3368 PlayToggle.prototype.handleClick = function handleClick(event) { 3369 if (this.player_.paused()) { 3370 this.player_.play(); 3371 } else { 3372 this.player_.pause(); 3373 } 3374 }; 3375 3376 /** 3377 * Add the vjs-playing class to the element so it can change appearance. 3378 * 3379 * @param {EventTarget~Event} [event] 3380 * The event that caused this function to run. 3381 * 3382 * @listens Player#play 3383 */ 3384 3385 3386 PlayToggle.prototype.handlePlay = function handlePlay(event) { 3387 this.removeClass('vjs-paused'); 3388 this.addClass('vjs-playing'); 3389 // change the button text to "Pause" 3390 this.controlText('Pause'); 3391 }; 3392 3393 /** 3394 * Add the vjs-paused class to the element so it can change appearance. 3395 * 3396 * @param {EventTarget~Event} [event] 3397 * The event that caused this function to run. 3398 * 3399 * @listens Player#pause 3400 */ 3401 3402 3403 PlayToggle.prototype.handlePause = function handlePause(event) { 3404 this.removeClass('vjs-playing'); 3405 this.addClass('vjs-paused'); 3406 // change the button text to "Play" 3407 this.controlText('Play'); 3408 }; 3409 3410 return PlayToggle; 3411}(_button2['default']); 3412 3413/** 3414 * The text that should display over the `PlayToggle`s controls. Added for localization. 3415 * 3416 * @type {string} 3417 * @private 3418 */ 3419 3420 3421PlayToggle.prototype.controlText_ = 'Play'; 3422 3423_component2['default'].registerComponent('PlayToggle', PlayToggle); 3424exports['default'] = PlayToggle; 3425 3426},{"2":2,"5":5}],13:[function(_dereq_,module,exports){ 3427'use strict'; 3428 3429exports.__esModule = true; 3430 3431var _menuButton = _dereq_(47); 3432 3433var _menuButton2 = _interopRequireDefault(_menuButton); 3434 3435var _menu = _dereq_(49); 3436 3437var _menu2 = _interopRequireDefault(_menu); 3438 3439var _playbackRateMenuItem = _dereq_(14); 3440 3441var _playbackRateMenuItem2 = _interopRequireDefault(_playbackRateMenuItem); 3442 3443var _component = _dereq_(5); 3444 3445var _component2 = _interopRequireDefault(_component); 3446 3447var _dom = _dereq_(81); 3448 3449var Dom = _interopRequireWildcard(_dom); 3450 3451function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 3452 3453function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 3454 3455function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 3456 3457function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 3458 3459function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 3460 * @file playback-rate-menu-button.js 3461 */ 3462 3463 3464/** 3465 * The component for controlling the playback rate. 3466 * 3467 * @extends MenuButton 3468 */ 3469var PlaybackRateMenuButton = function (_MenuButton) { 3470 _inherits(PlaybackRateMenuButton, _MenuButton); 3471 3472 /** 3473 * Creates an instance of this class. 3474 * 3475 * @param {Player} player 3476 * The `Player` that this class should be attached to. 3477 * 3478 * @param {Object} [options] 3479 * The key/value store of player options. 3480 */ 3481 function PlaybackRateMenuButton(player, options) { 3482 _classCallCheck(this, PlaybackRateMenuButton); 3483 3484 var _this = _possibleConstructorReturn(this, _MenuButton.call(this, player, options)); 3485 3486 _this.updateVisibility(); 3487 _this.updateLabel(); 3488 3489 _this.on(player, 'loadstart', _this.updateVisibility); 3490 _this.on(player, 'ratechange', _this.updateLabel); 3491 return _this; 3492 } 3493 3494 /** 3495 * Create the `Component`'s DOM element 3496 * 3497 * @return {Element} 3498 * The element that was created. 3499 */ 3500 3501 3502 PlaybackRateMenuButton.prototype.createEl = function createEl() { 3503 var el = _MenuButton.prototype.createEl.call(this); 3504 3505 this.labelEl_ = Dom.createEl('div', { 3506 className: 'vjs-playback-rate-value', 3507 innerHTML: 1.0 3508 }); 3509 3510 el.appendChild(this.labelEl_); 3511 3512 return el; 3513 }; 3514 3515 /** 3516 * Builds the default DOM `className`. 3517 * 3518 * @return {string} 3519 * The DOM `className` for this object. 3520 */ 3521 3522 3523 PlaybackRateMenuButton.prototype.buildCSSClass = function buildCSSClass() { 3524 return 'vjs-playback-rate ' + _MenuButton.prototype.buildCSSClass.call(this); 3525 }; 3526 3527 /** 3528 * Create the playback rate menu 3529 * 3530 * @return {Menu} 3531 * Menu object populated with {@link PlaybackRateMenuItem}s 3532 */ 3533 3534 3535 PlaybackRateMenuButton.prototype.createMenu = function createMenu() { 3536 var menu = new _menu2['default'](this.player()); 3537 var rates = this.playbackRates(); 3538 3539 if (rates) { 3540 for (var i = rates.length - 1; i >= 0; i--) { 3541 menu.addChild(new _playbackRateMenuItem2['default'](this.player(), { rate: rates[i] + 'x' })); 3542 } 3543 } 3544 3545 return menu; 3546 }; 3547 3548 /** 3549 * Updates ARIA accessibility attributes 3550 */ 3551 3552 3553 PlaybackRateMenuButton.prototype.updateARIAAttributes = function updateARIAAttributes() { 3554 // Current playback rate 3555 this.el().setAttribute('aria-valuenow', this.player().playbackRate()); 3556 }; 3557 3558 /** 3559 * This gets called when an `PlaybackRateMenuButton` is "clicked". See 3560 * {@link ClickableComponent} for more detailed information on what a click can be. 3561 * 3562 * @param {EventTarget~Event} [event]
3563 * The `keydown`, `tap`, or `click` event that caused this function to be 3564 * called. 3565 * 3566 * @listens tap 3567 * @listens click 3568 */ 3569 3570 3571 PlaybackRateMenuButton.prototype.handleClick = function handleClick(event) { 3572 // select next rate option 3573 var currentRate = this.player().playbackRate(); 3574 var rates = this.playbackRates(); 3575 3576 // this will select first one if the last one currently selected 3577 var newRate = rates[0]; 3578 3579 for (var i = 0; i < rates.length; i++) { 3580 if (rates[i] > currentRate) { 3581 newRate = rates[i]; 3582 break; 3583 } 3584 } 3585 this.player().playbackRate(newRate); 3586 }; 3587 3588 /** 3589 * Get possible playback rates 3590 * 3591 * @return {Array} 3592 * All possible playback rates 3593 */ 3594 3595 3596 PlaybackRateMenuButton.prototype.playbackRates = function playbackRates() { 3597 return this.options_.playbackRates || this.options_.playerOptions && this.options_.playerOptions.playbackRates; 3598 }; 3599 3600 /** 3601 * Get whether playback rates is supported by the tech 3602 * and an array of playback rates exists 3603 * 3604 * @return {boolean} 3605 * Whether changing playback rate is supported 3606 */ 3607 3608 3609 PlaybackRateMenuButton.prototype.playbackRateSupported = function playbackRateSupported() { 3610 return this.player().tech_ && this.player().tech_.featuresPlaybackRate && this.playbackRates() && this.playbackRates().length > 0; 3611 }; 3612 3613 /** 3614 * Hide playback rate controls when they're no playback rate options to select 3615 * 3616 * @param {EventTarget~Event} [event] 3617 * The event that caused this function to run. 3618 * 3619 * @listens Player#loadstart 3620 */ 3621 3622 3623 PlaybackRateMenuButton.prototype.updateVisibility = function updateVisibility(event) { 3624 if (this.playbackRateSupported()) { 3625 this.removeClass('vjs-hidden'); 3626 } else { 3627 this.addClass('vjs-hidden'); 3628 } 3629 }; 3630 3631 /** 3632 * Update button label when rate changed 3633 * 3634 * @param {EventTarget~Event} [event] 3635 * The event that caused this function to run. 3636 * 3637 * @listens Player#ratechange 3638 */ 3639 3640 3641 PlaybackRateMenuButton.prototype.updateLabel = function updateLabel(event) { 3642 if (this.playbackRateSupported()) { 3643 this.labelEl_.innerHTML = this.player().playbackRate() + 'x'; 3644 } 3645 }; 3646 3647 return PlaybackRateMenuButton; 3648}(_menuButton2['default']); 3649 3650/** 3651 * The text that should display over the `FullscreenToggle`s controls. Added for localization. 3652 * 3653 * @type {string} 3654 * @private 3655 */ 3656 3657 3658PlaybackRateMenuButton.prototype.controlText_ = 'Playback Rate'; 3659 3660_component2['default'].registerComponent('PlaybackRateMenuButton', PlaybackRateMenuButton); 3661exports['default'] = PlaybackRateMenuButton; 3662 3663},{"14":14,"47":47,"49":49,"5":5,"81":81}],14:[function(_dereq_,module,exports){ 3664'use strict'; 3665 3666exports.__esModule = true; 3667 3668var _menuItem = _dereq_(48); 3669 3670var _menuItem2 = _interopRequireDefault(_menuItem); 3671 3672var _component = _dereq_(5); 3673 3674var _component2 = _interopRequireDefault(_component); 3675 3676function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 3677 3678function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 3679 3680function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 3681 3682function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 3683 * @file playback-rate-menu-item.js 3684 */ 3685 3686 3687/** 3688 * The specific menu item type for selecting a playback rate. 3689 * 3690 * @extends MenuItem 3691 */ 3692var PlaybackRateMenuItem = function (_MenuItem) { 3693 _inherits(PlaybackRateMenuItem, _MenuItem); 3694 3695 /** 3696 * Creates an instance of this class. 3697 * 3698 * @param {Player} player 3699 * The `Player` that this class should be attached to. 3700 * 3701 * @param {Object} [options] 3702 * The key/value store of player options. 3703 */ 3704 function PlaybackRateMenuItem(player, options) { 3705 _classCallCheck(this, PlaybackRateMenuItem); 3706 3707 var label = options.rate; 3708 var rate = parseFloat(label, 10); 3709 3710 // Modify options for parent MenuItem class's init. 3711 options.label = label; 3712 options.selected = rate === 1; 3713 options.selectable = true; 3714 3715 var _this = _possibleConstructorReturn(this, _MenuItem.call(this, player, options)); 3716 3717 _this.label = label; 3718 _this.rate = rate; 3719 3720 _this.on(player, 'ratechange', _this.update); 3721 return _this; 3722 } 3723 3724 /** 3725 * This gets called when an `PlaybackRateMenuItem` is "clicked". See 3726 * {@link ClickableComponent} for more detailed information on what a click can be. 3727 * 3728 * @param {EventTarget~Event} [event]
3729 * The `keydown`, `tap`, or `click` event that caused this function to be 3730 * called. 3731 * 3732 * @listens tap 3733 * @listens click 3734 */ 3735 3736 3737 PlaybackRateMenuItem.prototype.handleClick = function handleClick(event) { 3738 _MenuItem.prototype.handleClick.call(this); 3739 this.player().playbackRate(this.rate); 3740 }; 3741 3742 /** 3743 * Update the PlaybackRateMenuItem when the playbackrate changes. 3744 * 3745 * @param {EventTarget~Event} [event] 3746 * The `ratechange` event that caused this function to run. 3747 * 3748 * @listens Player#ratechange 3749 */ 3750 3751 3752 PlaybackRateMenuItem.prototype.update = function update(event) { 3753 this.selected(this.player().playbackRate() === this.rate); 3754 }; 3755 3756 return PlaybackRateMenuItem; 3757}(_menuItem2['default']); 3758 3759/** 3760 * The text that should display over the `PlaybackRateMenuItem`s controls. Added for localization. 3761 * 3762 * @type {string} 3763 * @private 3764 */ 3765 3766 3767PlaybackRateMenuItem.prototype.contentElType = 'button'; 3768 3769_component2['default'].registerComponent('PlaybackRateMenuItem', PlaybackRateMenuItem); 3770exports['default'] = PlaybackRateMenuItem; 3771 3772},{"48":48,"5":5}],15:[function(_dereq_,module,exports){ 3773'use strict'; 3774 3775exports.__esModule = true; 3776 3777var _component = _dereq_(5); 3778 3779var _component2 = _interopRequireDefault(_component); 3780 3781var _dom = _dereq_(81); 3782 3783var Dom = _interopRequireWildcard(_dom); 3784 3785function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 3786 3787function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 3788 3789function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 3790 3791function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 3792 3793function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 3794 * @file load-progress-bar.js 3795 */ 3796 3797 3798/** 3799 * Shows loading progress 3800 * 3801 * @extends Component 3802 */ 3803var LoadProgressBar = function (_Component) { 3804 _inherits(LoadProgressBar, _Component); 3805 3806 /** 3807 * Creates an instance of this class. 3808 * 3809 * @param {Player} player 3810 * The `Player` that this class should be attached to. 3811 * 3812 * @param {Object} [options] 3813 * The key/value store of player options. 3814 */ 3815 function LoadProgressBar(player, options) { 3816 _classCallCheck(this, LoadProgressBar); 3817 3818 var _this = _possibleConstructorReturn(this, _Component.call(this, player, options)); 3819 3820 _this.partEls_ = []; 3821 _this.on(player, 'progress', _this.update); 3822 return _this; 3823 } 3824 3825 /** 3826 * Create the `Component`'s DOM element 3827 * 3828 * @return {Element} 3829 * The element that was created. 3830 */ 3831 3832 3833 LoadProgressBar.prototype.createEl = function createEl() { 3834 return _Component.prototype.createEl.call(this, 'div', { 3835 className: 'vjs-load-progress', 3836 innerHTML: '<span class="vjs-control-text"><span>' + this.localize('Loaded') + '</span>: 0%</span>' 3837 }); 3838 }; 3839 3840 /** 3841 * Update progress bar 3842 * 3843 * @param {EventTarget~Event} [event] 3844 * The `progress` event that caused this function to run. 3845 * 3846 * @listens Player#progress 3847 */ 3848 3849 3850 LoadProgressBar.prototype.update = function update(event) { 3851 var buffered = this.player_.buffered(); 3852 var duration = this.player_.duration(); 3853 var bufferedEnd = this.player_.bufferedEnd(); 3854 var children = this.partEls_; 3855 3856 // get the percent width of a time compared to the total end 3857 var percentify = function percentify(time, end) { 3858 // no NaN 3859 var percent = time / end || 0; 3860 3861 return (percent >= 1 ? 1 : percent) * 100 + '%'; 3862 }; 3863 3864 // update the width of the progress bar 3865 this.el_.style.width = percentify(bufferedEnd, duration); 3866 3867 // add child elements to represent the individual buffered time ranges 3868 for (var i = 0; i < buffered.length; i++) { 3869 var start = buffered.start(i); 3870 var end = buffered.end(i); 3871 var part = children[i]; 3872 3873 if (!part) { 3874 part = this.el_.appendChild(Dom.createEl()); 3875 children[i] = part;
3876 } 3877 3878 // set the percent based on the width of the progress bar (bufferedEnd) 3879 part.style.left = percentify(start, bufferedEnd); 3880 part.style.width = percentify(end - start, bufferedEnd); 3881 } 3882 3883 // remove unused buffered range elements 3884 for (var _i = children.length; _i > buffered.length; _i--) { 3885 this.el_.removeChild(children[_i - 1]); 3886 } 3887 children.length = buffered.length; 3888 }; 3889 3890 return LoadProgressBar; 3891}(_component2['default']); 3892 3893_component2['default'].registerComponent('LoadProgressBar', LoadProgressBar); 3894exports['default'] = LoadProgressBar; 3895 3896},{"5":5,"81":81}],16:[function(_dereq_,module,exports){ 3897'use strict'; 3898 3899exports.__esModule = true; 3900 3901var _component = _dereq_(5); 3902 3903var _component2 = _interopRequireDefault(_component); 3904 3905var _dom = _dereq_(81); 3906 3907var Dom = _interopRequireWildcard(_dom); 3908 3909var _fn = _dereq_(83); 3910 3911var Fn = _interopRequireWildcard(_fn); 3912 3913var _formatTime = _dereq_(84); 3914 3915var _formatTime2 = _interopRequireDefault(_formatTime); 3916 3917var _computedStyle = _dereq_(80); 3918 3919var _computedStyle2 = _interopRequireDefault(_computedStyle); 3920 3921function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 3922 3923function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 3924 3925function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 3926 3927function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 3928 3929function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 3930 * @file mouse-time-display.js 3931 */ 3932 3933 3934/** 3935 * The Mouse Time Display component shows the time you will seek to 3936 * when hovering over the progress bar 3937 * 3938 * @extends Component 3939 */ 3940var MouseTimeDisplay = function (_Component) { 3941 _inherits(MouseTimeDisplay, _Component); 3942 3943 /** 3944 * Creates an instance of this class. 3945 * 3946 * @param {Player} player 3947 * The `Player` that this class should be attached to. 3948 * 3949 * @param {Object} [options] 3950 * The key/value store of player options. 3951 */ 3952 function MouseTimeDisplay(player, options) { 3953 _classCallCheck(this, MouseTimeDisplay); 3954 3955 var _this = _possibleConstructorReturn(this, _Component.call(this, player, options)); 3956 3957 if (options.playerOptions && options.playerOptions.controlBar && options.playerOptions.controlBar.progressControl && options.playerOptions.controlBar.progressControl.keepTooltipsInside) { 3958 _this.keepTooltipsInside = options.playerOptions.controlBar.progressControl.keepTooltipsInside; 3959 } 3960 3961 if (_this.keepTooltipsInside) { 3962 _this.tooltip = Dom.createEl('div', { className: 'vjs-time-tooltip' }); 3963 _this.el().appendChild(_this.tooltip); 3964 _this.addClass('vjs-keep-tooltips-inside'); 3965 } 3966 3967 _this.update(0, 0); 3968 3969 player.on('ready', function () { 3970 _this.on(player.controlBar.progressControl.el(), 'mousemove', Fn.throttle(Fn.bind(_this, _this.handleMouseMove), 25)); 3971 }); 3972 return _this; 3973 } 3974 3975 /** 3976 * Create the `Component`'s DOM element 3977 * 3978 * @return {Element} 3979 * The element that was created. 3980 */ 3981 3982 3983 MouseTimeDisplay.prototype.createEl = function createEl() { 3984 return _Component.prototype.createEl.call(this, 'div', { 3985 className: 'vjs-mouse-display' 3986 }); 3987 }; 3988 3989 /** 3990 * Handle the mouse move event on the `MouseTimeDisplay`. 3991 * 3992 * @param {EventTarget~Event} event 3993 * The `mousemove` event that caused this to event to run. 3994 * 3995 * @listen mousemove 3996 */ 3997 3998 3999 MouseTimeDisplay.prototype.handleMouseMove = function handleMouseMove(event) { 4000 var duration = this.player_.duration(); 4001 var newTime = this.calculateDistance(event) * duration; 4002 var position = event.pageX - Dom.findElPosition(this.el().parentNode).left; 4003 4004 this.update(newTime, position); 4005 }; 4006 4007 /** 4008 * Update the time and posistion of the `MouseTimeDisplay`. 4009 * 4010 * @param {number} newTime 4011 * Time to change the `MouseTimeDisplay` to. 4012 * 4013 * @param {nubmer} position 4014 * Postion from the left of the in pixels. 4015 */ 4016 4017 4018 MouseTimeDisplay.prototype.update = function update(newTime, position) { 4019 var time = (0, _formatTime2['default'])(newTime, this.player_.duration()); 4020 4021 this.el().style.left = position + 'px'; 4022 this.el().setAttribute('data-current-time', time); 4023 4024 if (this.keepTooltipsInside) { 4025 var clampedPosition = this.clampPosition_(position); 4026 var difference = position - clampedPosition + 1; 4027 var tooltipWidth = parseFloat((0, _computedStyle2['default'])(this.tooltip, 'width')); 4028 var tooltipWidthHalf = tooltipWidth / 2; 4029 4030 this.tooltip.innerHTML = time; 4031 this.tooltip.style.right = '-' + (tooltipWidthHalf - difference) + 'px'; 4032 } 4033 }; 4034 4035 /** 4036 * Get the mouse pointers x coordinate in pixels. 4037 * 4038 * @param {EventTarget~Event} [event] 4039 * The `mousemove` event that was passed to this function by 4040 * {@link MouseTimeDisplay#handleMouseMove} 4041 * 4042 * @return {number} 4043 * THe x position in pixels of the mouse pointer. 4044 */ 4045 4046 4047 MouseTimeDisplay.prototype.calculateDistance = function calculateDistance(event) { 4048 return Dom.getPointerPosition(this.el().parentNode, event).x; 4049 }; 4050 4051 /** 4052 * This takes in a horizontal position for the bar and returns a clamped position. 4053 * Clamped position means that it will keep the position greater than half the width 4054 * of the tooltip and smaller than the player width minus half the width o the tooltip. 4055 * It will only clamp the position if `keepTooltipsInside` option is set. 4056 * 4057 * @param {number} position 4058 * The position the bar wants to be 4059 * 4060 * @return {number} 4061 * The (potentially) new clamped position. 4062 * 4063 * @private 4064 */ 4065 4066 4067 MouseTimeDisplay.prototype.clampPosition_ = function clampPosition_(position) { 4068 if (!this.keepTooltipsInside) { 4069 return position; 4070 } 4071 4072 var playerWidth = parseFloat((0, _computedStyle2['default'])(this.player().el(), 'width')); 4073 var tooltipWidth = parseFloat((0, _computedStyle2['default'])(this.tooltip, 'width')); 4074 var tooltipWidthHalf = tooltipWidth / 2; 4075 var actualPosition = position; 4076 4077 if (position < tooltipWidthHalf) { 4078 actualPosition = Math.ceil(tooltipWidthHalf); 4079 } else if (position > playerWidth - tooltipWidthHalf) { 4080 actualPosition = Math.floor(playerWidth - tooltipWidthHalf); 4081 } 4082 4083 return actualPosition; 4084 }; 4085 4086 return MouseTimeDisplay; 4087}(_component2['default']); 4088 4089_component2['default'].registerComponent('MouseTimeDisplay', MouseTimeDisplay); 4090exports['default'] = MouseTimeDisplay; 4091 4092},{"5":5,"80":80,"81":81,"83":83,"84":84}],17:[function(_dereq_,module,exports){ 4093'use strict'; 4094 4095exports.__esModule = true; 4096 4097var _component = _dereq_(5); 4098 4099var _component2 = _interopRequireDefault(_component); 4100 4101var _fn = _dereq_(83); 4102 4103var Fn = _interopRequireWildcard(_fn); 4104 4105var _formatTime = _dereq_(84); 4106 4107var _formatTime2 = _interopRequireDefault(_formatTime); 4108 4109function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 4110 4111function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 4112 4113function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 4114 4115function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 4116 4117function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
4117onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 4118 * @file play-progress-bar.js 4119 */ 4120 4121 4122/** 4123 * Shows play progress 4124 * 4125 * @extends Component 4126 */ 4127var PlayProgressBar = function (_Component) { 4128 _inherits(PlayProgressBar, _Component); 4129 4130 /** 4131 * Creates an instance of this class. 4132 * 4133 * @param {Player} player 4134 * The `Player` that this class should be attached to. 4135 * 4136 * @param {Object} [options] 4137 * The key/value store of player options. 4138 */ 4139 function PlayProgressBar(player, options) { 4140 _classCallCheck(this, PlayProgressBar); 4141 4142 var _this = _possibleConstructorReturn(this, _Component.call(this, player, options)); 4143 4144 _this.updateDataAttr(); 4145 _this.on(player, 'timeupdate', _this.updateDataAttr); 4146 player.ready(Fn.bind(_this, _this.updateDataAttr)); 4147 4148 if (options.playerOptions && options.playerOptions.controlBar && options.playerOptions.controlBar.progressControl && options.playerOptions.controlBar.progressControl.keepTooltipsInside) { 4149 _this.keepTooltipsInside = options.playerOptions.controlBar.progressControl.keepTooltipsInside; 4150 } 4151 4152 if (_this.keepTooltipsInside) { 4153 _this.addClass('vjs-keep-tooltips-inside'); 4154 } 4155 return _this; 4156 } 4157 4158 /** 4159 * Create the `Component`'s DOM element 4160 * 4161 * @return {Element} 4162 * The element that was created. 4163 */ 4164 4165 4166 PlayProgressBar.prototype.createEl = function createEl() { 4167 return _Component.prototype.createEl.call(this, 'div', { 4168 className: 'vjs-play-progress vjs-slider-bar', 4169 innerHTML: '<span class="vjs-control-text"><span>' + this.localize('Progress') + '</span>: 0%</span>' 4170 }); 4171 }; 4172 4173 /** 4174 * Update the data-current-time attribute on the `PlayProgressBar`. 4175 * 4176 * @param {EventTarget~Event} [event] 4177 * The `timeupdate` event that caused this to run. 4178 * 4179 * @listens Player#timeupdate 4180 */ 4181 4182 4183 PlayProgressBar.prototype.updateDataAttr = function updateDataAttr(event) { 4184 var time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime(); 4185 4186 this.el_.setAttribute('data-current-time', (0, _formatTime2['default'])(time, this.player_.duration())); 4187 }; 4188 4189 return PlayProgressBar; 4190}(_component2['default']); 4191 4192_component2['default'].registerComponent('PlayProgressBar', PlayProgressBar); 4193exports['default'] = PlayProgressBar; 4194 4195},{"5":5,"83":83,"84":84}],18:[function(_dereq_,module,exports){ 4196'use strict'; 4197 4198exports.__esModule = true; 4199 4200var _component = _dereq_(5); 4201 4202var _component2 = _interopRequireDefault(_component); 4203 4204_dereq_(19); 4205 4206_dereq_(16); 4207 4208function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 4209 4210function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 4211 4212function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 4213 4214function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
4214onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 4215 * @file progress-control.js 4216 */ 4217 4218 4219/** 4220 * The Progress Control component contains the seek bar, load progress, 4221 * and play progress. 4222 * 4223 * @extends Component 4224 */ 4225var ProgressControl = function (_Component) { 4226 _inherits(ProgressControl, _Component); 4227 4228 function ProgressControl() { 4229 _classCallCheck(this, ProgressControl); 4230 4231 return _possibleConstructorReturn(this, _Component.apply(this, arguments)); 4232 } 4233 4234 /** 4235 * Create the `Component`'s DOM element 4236 * 4237 * @return {Element} 4238 * The element that was created. 4239 */ 4240 ProgressControl.prototype.createEl = function createEl() { 4241 return _Component.prototype.createEl.call(this, 'div', { 4242 className: 'vjs-progress-control vjs-control' 4243 }); 4244 }; 4245 4246 return ProgressControl; 4247}(_component2['default']); 4248 4249/** 4250 * Default options for `ProgressControl` 4251 * 4252 * @type {Object} 4253 * @private 4254 */ 4255 4256 4257ProgressControl.prototype.options_ = { 4258 children: ['seekBar'] 4259}; 4260 4261_component2['default'].registerComponent('ProgressControl', ProgressControl); 4262exports['default'] = ProgressControl; 4263 4264},{"16":16,"19":19,"5":5}],19:[function(_dereq_,module,exports){ 4265'use strict'; 4266 4267exports.__esModule = true; 4268 4269var _slider = _dereq_(57); 4270 4271var _slider2 = _interopRequireDefault(_slider); 4272 4273var _component = _dereq_(5); 4274 4275var _component2 = _interopRequireDefault(_component); 4276 4277var _fn = _dereq_(83); 4278 4279var Fn = _interopRequireWildcard(_fn); 4280 4281var _formatTime = _dereq_(84); 4282 4283var _formatTime2 = _interopRequireDefault(_formatTime); 4284 4285var _computedStyle = _dereq_(80); 4286 4287var _computedStyle2 = _interopRequireDefault(_computedStyle); 4288 4289_dereq_(15); 4290 4291_dereq_(17); 4292 4293_dereq_(20); 4294 4295function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 4296 4297function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 4298 4299function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 4300 4301function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 4302 4303function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 4304 * @file seek-bar.js 4305 */ 4306 4307 4308/** 4309 * Seek Bar and holder for the progress bars 4310 * 4311 * @extends Slider 4312 */
4313var SeekBar = function (_Slider) { 4314 _inherits(SeekBar, _Slider); 4315 4316 /** 4317 * Creates an instance of this class. 4318 * 4319 * @param {Player} player 4320 * The `Player` that this class should be attached to. 4321 * 4322 * @param {Object} [options] 4323 * The key/value store of player options. 4324 */ 4325 function SeekBar(player, options) { 4326 _classCallCheck(this, SeekBar); 4327 4328 var _this = _possibleConstructorReturn(this, _Slider.call(this, player, options)); 4329 4330 _this.on(player, 'timeupdate', _this.updateProgress); 4331 _this.on(player, 'ended', _this.updateProgress); 4332 player.ready(Fn.bind(_this, _this.updateProgress)); 4333 4334 if (options.playerOptions && options.playerOptions.controlBar && options.playerOptions.controlBar.progressControl && options.playerOptions.controlBar.progressControl.keepTooltipsInside) { 4335 _this.keepTooltipsInside = options.playerOptions.controlBar.progressControl.keepTooltipsInside; 4336 } 4337 4338 if (_this.keepTooltipsInside) { 4339 _this.tooltipProgressBar = _this.addChild('TooltipProgressBar'); 4340 } 4341 return _this; 4342 } 4343 4344 /** 4345 * Create the `Component`'s DOM element 4346 * 4347 * @return {Element} 4348 * The element that was created. 4349 */ 4350 4351 4352 SeekBar.prototype.createEl = function createEl() { 4353 return _Slider.prototype.createEl.call(this, 'div', { 4354 className: 'vjs-progress-holder' 4355 }, { 4356 'aria-label': 'progress bar' 4357 }); 4358 }; 4359 4360 /** 4361 * Update the seek bars tooltip and width. 4362 * 4363 * @param {EventTarget~Event} [event] 4364 * The `timeupdate` or `ended` event that caused this to run. 4365 * 4366 * @listens Player#timeupdate 4367 * @listens Player#ended 4368 */ 4369 4370 4371 SeekBar.prototype.updateProgress = function updateProgress(event) { 4372 this.updateAriaAttributes(this.el_); 4373 4374 if (this.keepTooltipsInside) { 4375 this.updateAriaAttributes(this.tooltipProgressBar.el_); 4376 this.tooltipProgressBar.el_.style.width = this.bar.el_.style.width; 4377 4378 var playerWidth = parseFloat((0, _computedStyle2['default'])(this.player().el(), 'width')); 4379 var tooltipWidth = parseFloat((0, _computedStyle2['default'])(this.tooltipProgressBar.tooltip, 'width')); 4380 var tooltipStyle = this.tooltipProgressBar.el().style; 4381 4382 tooltipStyle.maxWidth = Math.floor(playerWidth - tooltipWidth / 2) + 'px'; 4383 tooltipStyle.minWidth = Math.ceil(tooltipWidth / 2) + 'px'; 4384 tooltipStyle.right = '-' + tooltipWidth / 2 + 'px'; 4385 } 4386 }; 4387 4388 /** 4389 * Update ARIA accessibility attributes 4390 * 4391 * @param {Element} el 4392 * The element to update with aria accessibility attributes. 4393 */ 4394 4395 4396 SeekBar.prototype.updateAriaAttributes = function updateAriaAttributes(el) { 4397 // Allows for smooth scrubbing, when player can't keep up. 4398 var time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime(); 4399 4400 // machine readable value of progress bar (percentage complete) 4401 el.setAttribute('aria-valuenow', (this.getPercent() * 100).toFixed(2)); 4402 // human readable value of progress bar (time complete) 4403 el.setAttribute('aria-valuetext', (0, _formatTime2['default'])(time, this.player_.duration())); 4404 }; 4405 4406 /** 4407 * Get percentage of video played 4408 * 4409 * @return {number} 4410 * The percentage played 4411 */ 4412 4413 4414 SeekBar.prototype.getPercent = function getPercent() { 4415 var percent = this.player_.currentTime() / this.player_.duration(); 4416 4417 return percent >= 1 ? 1 : percent; 4418 }; 4419 4420 /** 4421 * Handle mouse down on seek bar 4422 * 4423 * @param {EventTarget~Event} event 4424 * The `mousedown` event that caused this to run. 4425 * 4426 * @listens mousedown 4427 */ 4428 4429 4430 SeekBar.prototype.handleMouseDown = function handleMouseDown(event) { 4431 this.player_.scrubbing(true); 4432 4433 this.videoWasPlaying = !this.player_.paused(); 4434 this.player_.pause(); 4435 4436 _Slider.prototype.handleMouseDown.call(this, event); 4437 }; 4438 4439 /** 4440 * Handle mouse move on seek bar 4441 * 4442 * @param {EventTarget~Event} event 4443 * The `mousemove` event that caused this to run. 4444 * 4445 * @listens mousemove 4446 */ 4447 4448 4449 SeekBar.prototype.handleMouseMove = function handleMouseMove(event) { 4450 var newTime = this.calculateDistance(event) * this.player_.duration(); 4451 4452 // Don't let video end while scrubbing. 4453 if (newTime === this.player_.duration()) { 4454 newTime = newTime - 0.1; 4455 } 4456 4457 // Set new time (tell player to seek to new time) 4458 this.player_.currentTime(newTime); 4459 }; 4460 4461 /** 4462 * Handle mouse up on seek bar 4463 * 4464 * @param {EventTarget~Event} event 4465 * The `mouseup` event that caused this to run. 4466 * 4467 * @listens mouseup 4468 */ 4469 4470 4471 SeekBar.prototype.handleMouseUp = function handleMouseUp(event) { 4472 _Slider.prototype.handleMouseUp.call(this, event); 4473 4474 this.player_.scrubbing(false); 4475 if (this.videoWasPlaying) { 4476 this.player_.play(); 4477 } 4478 }; 4479 4480 /** 4481 * Move more quickly fast forward for keyboard-only users 4482 */ 4483 4484 4485 SeekBar.prototype.stepForward = function stepForward() { 4486 // more quickly fast forward for keyboard-only users 4487 this.player_.currentTime(this.player_.currentTime() + 5); 4488 }; 4489 4490 /** 4491 * Move more quickly rewind for keyboard-only users 4492 */ 4493 4494 4495 SeekBar.prototype.stepBack = function stepBack() { 4496 // more quickly rewind for keyboard-only users 4497 this.player_.currentTime(this.player_.currentTime() - 5); 4498 }; 4499 4500 return SeekBar; 4501}(_slider2['default']); 4502 4503/** 4504 * Default options for the `SeekBar` 4505 * 4506 * @type {Object} 4507 * @private 4508 */ 4509 4510 4511SeekBar.prototype.options_ = { 4512 children: ['loadProgressBar', 'mouseTimeDisplay', 'playProgressBar'], 4513 barName: 'playProgressBar' 4514}; 4515 4516/** 4517 * Call the update event for this Slider when this event happens on the player. 4518 * 4519 * @type {string} 4520 */ 4521SeekBar.prototype.playerEvent = 'timeupdate'; 4522 4523_component2['default'].registerComponent('SeekBar', SeekBar); 4524exports['default'] = SeekBar; 4525 4526},{"15":15,"17":17,"20":20,"5":5,"57":57,"80":80,"83":83,"84":84}],20:[function(_dereq_,module,exports){ 4527'use strict'; 4528 4529exports.__esModule = true; 4530 4531var _component = _dereq_(5); 4532 4533var _component2 = _interopRequireDefault(_component); 4534 4535var _fn = _dereq_(83); 4536 4537var Fn = _interopRequireWildcard(_fn); 4538 4539var _formatTime = _dereq_(84); 4540 4541var _formatTime2 = _interopRequireDefault(_formatTime); 4542 4543function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 4544 4545function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 4546 4547function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 4548 4549function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 4550 4551function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
4551onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 4552 * @file play-progress-bar.js 4553 */ 4554 4555 4556/** 4557 * Shows play progress 4558 * 4559 * @extends Component 4560 */ 4561var TooltipProgressBar = function (_Component) { 4562 _inherits(TooltipProgressBar, _Component); 4563 4564 /** 4565 * Creates an instance of this class. 4566 * 4567 * @param {Player} player 4568 * The `Player` that this class should be attached to. 4569 * 4570 * @param {Object} [options] 4571 * The key/value store of player options. 4572 */ 4573 function TooltipProgressBar(player, options) { 4574 _classCallCheck(this, TooltipProgressBar); 4575 4576 var _this = _possibleConstructorReturn(this, _Component.call(this, player, options)); 4577 4578 _this.updateDataAttr(); 4579 _this.on(player, 'timeupdate', _this.updateDataAttr); 4580 player.ready(Fn.bind(_this, _this.updateDataAttr)); 4581 return _this; 4582 } 4583 4584 /** 4585 * Create the `Component`'s DOM element 4586 * 4587 * @return {Element} 4588 * The element that was created. 4589 */ 4590 4591 4592 TooltipProgressBar.prototype.createEl = function createEl() { 4593 var el = _Component.prototype.createEl.call(this, 'div', { 4594 className: 'vjs-tooltip-progress-bar vjs-slider-bar', 4595 innerHTML: '<div class="vjs-time-tooltip"></div>\n <span class="vjs-control-text"><span>' + this.localize('Progress') + '</span>: 0%</span>' 4596 }); 4597 4598 this.tooltip = el.querySelector('.vjs-time-tooltip'); 4599 4600 return el; 4601 }; 4602 4603 /** 4604 * Updatet the data-current-time attribute for TooltipProgressBar 4605 * 4606 * @param {EventTarget~Event} [event] 4607 * The `timeupdate` event that caused this function to run. 4608 * 4609 * @listens Player#timeupdate 4610 */ 4611 4612 4613 TooltipProgressBar.prototype.updateDataAttr = function updateDataAttr(event) { 4614 var time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime(); 4615 var formattedTime = (0, _formatTime2['default'])(time, this.player_.duration()); 4616 4617 this.el_.setAttribute('data-current-time', formattedTime); 4618 this.tooltip.innerHTML = formattedTime; 4619 }; 4620 4621 return TooltipProgressBar; 4622}(_component2['default']); 4623 4624_component2['default'].registerComponent('TooltipProgressBar', TooltipProgressBar); 4625exports['default'] = TooltipProgressBar; 4626 4627},{"5":5,"83":83,"84":84}],21:[function(_dereq_,module,exports){ 4628'use strict'; 4629 4630exports.__esModule = true; 4631 4632var _spacer = _dereq_(22); 4633 4634var _spacer2 = _interopRequireDefault(_spacer); 4635 4636var _component = _dereq_(5); 4637 4638var _component2 = _interopRequireDefault(_component); 4639 4640function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 4641 4642function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 4643 4644function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 4645 4646function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
4646onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 4647 * @file custom-control-spacer.js 4648 */ 4649 4650 4651/** 4652 * Spacer specifically meant to be used as an insertion point for new plugins, etc. 4653 * 4654 * @extends Spacer 4655 */ 4656var CustomControlSpacer = function (_Spacer) { 4657 _inherits(CustomControlSpacer, _Spacer); 4658 4659 function CustomControlSpacer() { 4660 _classCallCheck(this, CustomControlSpacer); 4661 4662 return _possibleConstructorReturn(this, _Spacer.apply(this, arguments)); 4663 } 4664 4665 /** 4666 * Builds the default DOM `className`. 4667 * 4668 * @return {string} 4669 * The DOM `className` for this object. 4670 */ 4671 CustomControlSpacer.prototype.buildCSSClass = function buildCSSClass() { 4672 return 'vjs-custom-control-spacer ' + _Spacer.prototype.buildCSSClass.call(this); 4673 }; 4674 4675 /** 4676 * Create the `Component`'s DOM element 4677 * 4678 * @return {Element} 4679 * The element that was created. 4680 */ 4681 4682 4683 CustomControlSpacer.prototype.createEl = function createEl() { 4684 var el = _Spacer.prototype.createEl.call(this, { 4685 className: this.buildCSSClass() 4686 }); 4687 4688 // No-flex/table-cell mode requires there be some content 4689 // in the cell to fill the remaining space of the table. 4690 el.innerHTML = ' '; 4691 return el; 4692 }; 4693 4694 return CustomControlSpacer; 4695}(_spacer2['default']); 4696 4697_component2['default'].registerComponent('CustomControlSpacer', CustomControlSpacer); 4698exports['default'] = CustomControlSpacer; 4699 4700},{"22":22,"5":5}],22:[function(_dereq_,module,exports){ 4701'use strict'; 4702 4703exports.__esModule = true; 4704 4705var _component = _dereq_(5); 4706 4707var _component2 = _interopRequireDefault(_component); 4708 4709function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 4710 4711function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 4712 4713function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 4714 4715function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 4716 * @file spacer.js 4717 */ 4718 4719 4720/** 4721 * Just an empty spacer element that can be used as an append point for plugins, etc. 4722 * Also can be used to create space between elements when necessary. 4723 * 4724 * @extends Component 4725 */ 4726var Spacer = function (_Component) { 4727 _inherits(Spacer, _Component); 4728 4729 function Spacer() { 4730 _classCallCheck(this, Spacer); 4731 4732 return _possibleConstructorReturn(this, _Component.apply(this, arguments)); 4733 } 4734 4735 /** 4736 * Builds the default DOM `className`. 4737 * 4738 * @return {string} 4739 * The DOM `className` for this object. 4740 */ 4741 Spacer.prototype.buildCSSClass = function buildCSSClass() { 4742 return 'vjs-spacer ' + _Component.prototype.buildCSSClass.call(this); 4743 }; 4744 4745 /** 4746 * Create the `Component`'s DOM element 4747 * 4748 * @return {Element} 4749 * The element that was created. 4750 */ 4751 4752 4753 Spacer.prototype.createEl = function createEl() { 4754 return _Component.prototype.createEl.call(this, 'div', { 4755 className: this.buildCSSClass() 4756 }); 4757 }; 4758 4759 return Spacer; 4760}(_component2['default']); 4761 4762_component2['default'].registerComponent('Spacer', Spacer); 4763 4764exports['default'] = Spacer; 4765 4766},{"5":5}],23:[function(_dereq_,module,exports){ 4767'use strict'; 4768 4769exports.__esModule = true; 4770 4771var _textTrackMenuItem = _dereq_(31); 4772 4773var _textTrackMenuItem2 = _interopRequireDefault(_textTrackMenuItem); 4774 4775var _component = _dereq_(5); 4776 4777var _component2 = _interopRequireDefault(_component); 4778 4779function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 4780 4781function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 4782 4783function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 4784 4785function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
4785onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 4786 * @file caption-settings-menu-item.js 4787 */ 4788 4789 4790/** 4791 * The menu item for caption track settings menu 4792 * 4793 * @extends TextTrackMenuItem 4794 */ 4795var CaptionSettingsMenuItem = function (_TextTrackMenuItem) { 4796 _inherits(CaptionSettingsMenuItem, _TextTrackMenuItem); 4797 4798 /** 4799 * Creates an instance of this class. 4800 * 4801 * @param {Player} player 4802 * The `Player` that this class should be attached to. 4803 * 4804 * @param {Object} [options] 4805 * The key/value store of player options. 4806 */ 4807 function CaptionSettingsMenuItem(player, options) { 4808 _classCallCheck(this, CaptionSettingsMenuItem); 4809 4810 options.track = { 4811 player: player, 4812 kind: options.kind, 4813 label: options.kind + ' settings', 4814 selectable: false, 4815 'default': false, 4816 mode: 'disabled' 4817 }; 4818 4819 // CaptionSettingsMenuItem has no concept of 'selected' 4820 options.selectable = false; 4821 4822 var _this = _possibleConstructorReturn(this, _TextTrackMenuItem.call(this, player, options)); 4823 4824 _this.addClass('vjs-texttrack-settings'); 4825 _this.controlText(', opens ' + options.kind + ' settings dialog'); 4826 return _this; 4827 } 4828 4829 /** 4830 * This gets called when an `CaptionSettingsMenuItem` is "clicked". See 4831 * {@link ClickableComponent} for more detailed information on what a click can be. 4832 * 4833 * @param {EventTarget~Event} [event] 4834 * The `keydown`, `tap`, or `click` event that caused this function to be 4835 * called. 4836 * 4837 * @listens tap 4838 * @listens click 4839 */ 4840 4841 4842 CaptionSettingsMenuItem.prototype.handleClick = function handleClick(event) { 4843 this.player().getChild('textTrackSettings').show(); 4844 this.player().getChild('textTrackSettings').el_.focus(); 4845 }; 4846 4847 return CaptionSettingsMenuItem; 4848}(_textTrackMenuItem2['default']); 4849 4850_component2['default'].registerComponent('CaptionSettingsMenuItem', CaptionSettingsMenuItem); 4851exports['default'] = CaptionSettingsMenuItem; 4852 4853},{"31":31,"5":5}],24:[function(_dereq_,module,exports){ 4854'use strict'; 4855 4856exports.__esModule = true; 4857 4858var _textTrackButton = _dereq_(30); 4859 4860var _textTrackButton2 = _interopRequireDefault(_textTrackButton); 4861 4862var _component = _dereq_(5); 4863 4864var _component2 = _interopRequireDefault(_component); 4865 4866var _captionSettingsMenuItem = _dereq_(23); 4867 4868var _captionSettingsMenuItem2 = _interopRequireDefault(_captionSettingsMenuItem); 4869 4870function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 4871 4872function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 4873 4874function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 4875 4876function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
4876onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 4877 * @file captions-button.js 4878 */ 4879 4880 4881/** 4882 * The button component for toggling and selecting captions 4883 * 4884 * @extends TextTrackButton 4885 */ 4886var CaptionsButton = function (_TextTrackButton) { 4887 _inherits(CaptionsButton, _TextTrackButton); 4888 4889 /** 4890 * Creates an instance of this class. 4891 * 4892 * @param {Player} player 4893 * The `Player` that this class should be attached to. 4894 * 4895 * @param {Object} [options] 4896 * The key/value store of player options. 4897 * 4898 * @param {Component~ReadyCallback} [ready] 4899 * The function to call when this component is ready. 4900 */ 4901 function CaptionsButton(player, options, ready) { 4902 _classCallCheck(this, CaptionsButton); 4903 4904 var _this = _possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready)); 4905 4906 _this.el_.setAttribute('aria-label', 'Captions Menu'); 4907 return _this; 4908 } 4909 4910 /** 4911 * Builds the default DOM `className`. 4912 * 4913 * @return {string} 4914 * The DOM `className` for this object. 4915 */ 4916 4917 4918 CaptionsButton.prototype.buildCSSClass = function buildCSSClass() { 4919 return 'vjs-captions-button ' + _TextTrackButton.prototype.buildCSSClass.call(this); 4920 }; 4921 4922 /** 4923 * Create caption menu items 4924 * 4925 * @return {CaptionSettingsMenuItem[]} 4926 * The array of current menu items. 4927 */ 4928 4929 4930 CaptionsButton.prototype.createItems = function createItems() { 4931 var items = []; 4932 4933 if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks)) { 4934 items.push(new _captionSettingsMenuItem2['default'](this.player_, { kind: this.kind_ })); 4935 4936 this.hideThreshold_ += 1; 4937 } 4938 4939 return _TextTrackButton.prototype.createItems.call(this, items); 4940 }; 4941 4942 return CaptionsButton; 4943}(_textTrackButton2['default']); 4944 4945/** 4946 * `kind` of TextTrack to look for to associate it with this menu. 4947 * 4948 * @type {string} 4949 * @private 4950 */ 4951 4952 4953CaptionsButton.prototype.kind_ = 'captions'; 4954 4955/** 4956 * The text that should display over the `CaptionsButton`s controls. Added for localization. 4957 * 4958 * @type {string} 4959 * @private 4960 */ 4961CaptionsButton.prototype.controlText_ = 'Captions'; 4962 4963_component2['default'].registerComponent('CaptionsButton', CaptionsButton); 4964exports['default'] = CaptionsButton; 4965 4966},{"23":23,"30":30,"5":5}],25:[function(_dereq_,module,exports){ 4967'use strict'; 4968 4969exports.__esModule = true; 4970 4971var _textTrackButton = _dereq_(30); 4972 4973var _textTrackButton2 = _interopRequireDefault(_textTrackButton); 4974 4975var _component = _dereq_(5); 4976 4977var _component2 = _interopRequireDefault(_component); 4978 4979var _chaptersTrackMenuItem = _dereq_(26); 4980 4981var _chaptersTrackMenuItem2 = _interopRequireDefault(_chaptersTrackMenuItem); 4982 4983var _toTitleCase = _dereq_(91); 4984 4985var _toTitleCase2 = _interopRequireDefault(_toTitleCase); 4986 4987function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 4988 4989function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 4990 4991function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 4992 4993function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
vendor: 4,377 bytes, lines 4993-5110
4993onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 4994 * @file chapters-button.js 4995 */ 4996 4997 4998/** 4999 * The button component for toggling and selecting chapters 5000 * Chapters act much differently than other text tracks 5001 * Cues are navigation vs. other tracks of alternative languages 5002 * 5003 * @extends TextTrackButton 5004 */ 5005var ChaptersButton = function (_TextTrackButton) { 5006 _inherits(ChaptersButton, _TextTrackButton); 5007 5008 /** 5009 * Creates an instance of this class. 5010 * 5011 * @param {Player} player 5012 * The `Player` that this class should be attached to. 5013 * 5014 * @param {Object} [options] 5015 * The key/value store of player options. 5016 * 5017 * @param {Component~ReadyCallback} [ready] 5018 * The function to call when this function is ready. 5019 */ 5020 function ChaptersButton(player, options, ready) { 5021 _classCallCheck(this, ChaptersButton); 5022 5023 var _this = _possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready)); 5024 5025 _this.el_.setAttribute('aria-label', 'Chapters Menu'); 5026 return _this; 5027 } 5028 5029 /** 5030 * Builds the default DOM `className`. 5031 * 5032 * @return {string} 5033 * The DOM `className` for this object. 5034 */ 5035 5036 5037 ChaptersButton.prototype.buildCSSClass = function buildCSSClass() { 5038 return 'vjs-chapters-button ' + _TextTrackButton.prototype.buildCSSClass.call(this); 5039 }; 5040 5041 /** 5042 * Update the menu based on the current state of its items. 5043 * 5044 * @param {EventTarget~Event} [event] 5045 * An event that triggered this function to run. 5046 * 5047 * @listens TextTrackList#addtrack 5048 * @listens TextTrackList#removetrack 5049 * @listens TextTrackList#change 5050 */ 5051 5052 5053 ChaptersButton.prototype.update = function update(event) { 5054 if (!this.track_ || event && (event.type === 'addtrack' || event.type === 'removetrack')) { 5055 this.setTrack(this.findChaptersTrack()); 5056 } 5057 _TextTrackButton.prototype.update.call(this); 5058 }; 5059 5060 /** 5061 * Set the currently selected track for the chapters button. 5062 * 5063 * @param {TextTrack} track 5064 * The new track to select. Nothing will change if this is the currently selected 5065 * track. 5066 */ 5067 5068 5069 ChaptersButton.prototype.setTrack = function setTrack(track) { 5070 if (this.track_ === track) { 5071 return; 5072 } 5073 5074 if (!this.updateHandler_) { 5075 this.updateHandler_ = this.update.bind(this); 5076 } 5077 5078 // here this.track_ refers to the old track instance 5079 if (this.track_) { 5080 var remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_); 5081 5082 if (remoteTextTrackEl) { 5083 remoteTextTrackEl.removeEventListener('load', this.updateHandler_); 5084 } 5085 5086 this.track_ = null; 5087 } 5088 5089 this.track_ = track; 5090 5091 // here this.track_ refers to the new track instance 5092 if (this.track_) { 5093 this.track_.mode = 'hidden'; 5094 5095 var _remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_); 5096 5097 if (_remoteTextTrackEl) { 5098 _remoteTextTrackEl.addEventListener('load', this.updateHandler_); 5099 } 5100 } 5101 }; 5102 5103 /** 5104 * Find the track object that is currently in use by this ChaptersButton 5105 * 5106 * @return {TextTrack|undefined} 5107 * The current track or undefined if none was found. 5108 */ 5109 5110
5111 ChaptersButton.prototype.findChaptersTrack = function findChaptersTrack() { 5112 var tracks = this.player_.textTracks() || []; 5113 5114 for (var i = tracks.length - 1; i >= 0; i--) { 5115 // We will always choose the last track as our chaptersTrack 5116 var track = tracks[i]; 5117 5118 if (track.kind === this.kind_) { 5119 return track; 5120 } 5121 } 5122 }; 5123 5124 /** 5125 * Get the caption for the ChaptersButton based on the track label. This will also 5126 * use the current tracks localized kind as a fallback if a label does not exist. 5127 * 5128 * @return {string} 5129 * The tracks current label or the localized track kind. 5130 */ 5131 5132 5133 ChaptersButton.prototype.getMenuCaption = function getMenuCaption() { 5134 if (this.track_ && this.track_.label) { 5135 return this.track_.label; 5136 } 5137 return this.localize((0, _toTitleCase2['default'])(this.kind_)); 5138 }; 5139 5140 /** 5141 * Create menu from chapter track 5142 * 5143 * @return {Menu} 5144 * New menu for the chapter buttons 5145 */ 5146 5147 5148 ChaptersButton.prototype.createMenu = function createMenu() { 5149 this.options_.title = this.getMenuCaption(); 5150 return _TextTrackButton.prototype.createMenu.call(this); 5151 }; 5152 5153 /** 5154 * Create a menu item for each text track 5155 * 5156 * @return {TextTrackMenuItem[]} 5157 * Array of menu items 5158 */ 5159 5160 5161 ChaptersButton.prototype.createItems = function createItems() { 5162 var items = []; 5163 5164 if (!this.track_) { 5165 return items; 5166 } 5167 5168 var cues = this.track_.cues; 5169 5170 if (!cues) { 5171 return items; 5172 } 5173 5174 for (var i = 0, l = cues.length; i < l; i++) { 5175 var cue = cues[i]; 5176 var mi = new _chaptersTrackMenuItem2['default'](this.player_, { track: this.track_, cue: cue }); 5177 5178 items.push(mi); 5179 } 5180 5181 return items; 5182 }; 5183 5184 return ChaptersButton; 5185}(_textTrackButton2['default']); 5186 5187/** 5188 * `kind` of TextTrack to look for to associate it with this menu. 5189 * 5190 * @type {string} 5191 * @private 5192 */ 5193 5194 5195ChaptersButton.prototype.kind_ = 'chapters'; 5196 5197/** 5198 * The text that should display over the `ChaptersButton`s controls. Added for localization. 5199 * 5200 * @type {string} 5201 * @private 5202 */ 5203ChaptersButton.prototype.controlText_ = 'Chapters'; 5204 5205_component2['default'].registerComponent('ChaptersButton', ChaptersButton); 5206exports['default'] = ChaptersButton; 5207 5208},{"26":26,"30":30,"5":5,"91":91}],26:[function(_dereq_,module,exports){ 5209'use strict'; 5210 5211exports.__esModule = true; 5212 5213var _menuItem = _dereq_(48); 5214 5215var _menuItem2 = _interopRequireDefault(_menuItem); 5216 5217var _component = _dereq_(5); 5218 5219var _component2 = _interopRequireDefault(_component); 5220 5221var _fn = _dereq_(83); 5222 5223var Fn = _interopRequireWildcard(_fn); 5224 5225function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 5226 5227function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 5228 5229function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 5230 5231function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 5232 5233function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 5234 * @file chapters-track-menu-item.js 5235 */ 5236 5237 5238/** 5239 * The chapter track menu item 5240 * 5241 * @extends MenuItem 5242 */ 5243var ChaptersTrackMenuItem = function (_MenuItem) { 5244 _inherits(ChaptersTrackMenuItem, _MenuItem); 5245 5246 /** 5247 * Creates an instance of this class. 5248 * 5249 * @param {Player} player 5250 * The `Player` that this class should be attached to. 5251 * 5252 * @param {Object} [options] 5253 * The key/value store of player options. 5254 */ 5255 function ChaptersTrackMenuItem(player, options) { 5256 _classCallCheck(this, ChaptersTrackMenuItem); 5257 5258 var track = options.track; 5259 var cue = options.cue; 5260 var currentTime = player.currentTime(); 5261 5262 // Modify options for parent MenuItem class's init. 5263 options.selectable = true; 5264 options.label = cue.text; 5265 options.selected = cue.startTime <= currentTime && currentTime < cue.endTime; 5266 5267 var _this = _possibleConstructorReturn(this, _MenuItem.call(this, player, options)); 5268 5269 _this.track = track; 5270 _this.cue = cue; 5271 track.addEventListener('cuechange', Fn.bind(_this, _this.update)); 5272 return _this; 5273 } 5274 5275 /** 5276 * This gets called when an `ChaptersTrackMenuItem` is "clicked". See 5277 * {@link ClickableComponent} for more detailed information on what a click can be. 5278 * 5279 * @param {EventTarget~Event} [event]
5280 * The `keydown`, `tap`, or `click` event that caused this function to be 5281 * called. 5282 * 5283 * @listens tap 5284 * @listens click 5285 */ 5286 5287 5288 ChaptersTrackMenuItem.prototype.handleClick = function handleClick(event) { 5289 _MenuItem.prototype.handleClick.call(this); 5290 this.player_.currentTime(this.cue.startTime); 5291 this.update(this.cue.startTime); 5292 }; 5293 5294 /** 5295 * Update chapter menu item 5296 * 5297 * @param {EventTarget~Event} [event] 5298 * The `cuechange` event that caused this function to run. 5299 * 5300 * @listens TextTrack#cuechange 5301 */ 5302 5303 5304 ChaptersTrackMenuItem.prototype.update = function update(event) { 5305 var cue = this.cue; 5306 var currentTime = this.player_.currentTime(); 5307 5308 // vjs.log(currentTime, cue.startTime); 5309 this.selected(cue.startTime <= currentTime && currentTime < cue.endTime); 5310 }; 5311 5312 return ChaptersTrackMenuItem; 5313}(_menuItem2['default']); 5314 5315_component2['default'].registerComponent('ChaptersTrackMenuItem', ChaptersTrackMenuItem); 5316exports['default'] = ChaptersTrackMenuItem; 5317 5318},{"48":48,"5":5,"83":83}],27:[function(_dereq_,module,exports){ 5319'use strict'; 5320 5321exports.__esModule = true; 5322 5323var _textTrackButton = _dereq_(30); 5324 5325var _textTrackButton2 = _interopRequireDefault(_textTrackButton); 5326 5327var _component = _dereq_(5); 5328 5329var _component2 = _interopRequireDefault(_component); 5330 5331var _fn = _dereq_(83); 5332 5333var Fn = _interopRequireWildcard(_fn); 5334 5335function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 5336 5337function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 5338 5339function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 5340 5341function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 5342 5343function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 5344 * @file descriptions-button.js 5345 */ 5346 5347 5348/** 5349 * The button component for toggling and selecting descriptions 5350 * 5351 * @extends TextTrackButton 5352 */ 5353var DescriptionsButton = function (_TextTrackButton) { 5354 _inherits(DescriptionsButton, _TextTrackButton); 5355 5356 /** 5357 * Creates an instance of this class. 5358 * 5359 * @param {Player} player 5360 * The `Player` that this class should be attached to. 5361 * 5362 * @param {Object} [options] 5363 * The key/value store of player options. 5364 * 5365 * @param {Component~ReadyCallback} [ready] 5366 * The function to call when this component is ready. 5367 */ 5368 function DescriptionsButton(player, options, ready) { 5369 _classCallCheck(this, DescriptionsButton); 5370 5371 var _this = _possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready)); 5372 5373 _this.el_.setAttribute('aria-label', 'Descriptions Menu'); 5374 5375 var tracks = player.textTracks(); 5376 5377 if (tracks) { 5378 var changeHandler = Fn.bind(_this, _this.handleTracksChange); 5379 5380 tracks.addEventListener('change', changeHandler); 5381 _this.on('dispose', function () { 5382 tracks.removeEventListener('change', changeHandler); 5383 }); 5384 } 5385 return _this; 5386 } 5387 5388 /** 5389 * Handle text track change 5390 * 5391 * @param {EventTarget~Event} event 5392 * The event that caused this function to run 5393 * 5394 * @listens TextTrackList#change 5395 */ 5396 5397 5398 DescriptionsButton.prototype.handleTracksChange = function handleTracksChange(event) { 5399 var tracks = this.player().textTracks(); 5400 var disabled = false;
5401 5402 // Check whether a track of a different kind is showing 5403 for (var i = 0, l = tracks.length; i < l; i++) { 5404 var track = tracks[i]; 5405 5406 if (track.kind !== this.kind_ && track.mode === 'showing') { 5407 disabled = true; 5408 break; 5409 } 5410 } 5411 5412 // If another track is showing, disable this menu button 5413 if (disabled) { 5414 this.disable(); 5415 } else { 5416 this.enable(); 5417 } 5418 }; 5419 5420 /** 5421 * Builds the default DOM `className`. 5422 * 5423 * @return {string} 5424 * The DOM `className` for this object. 5425 */ 5426 5427 5428 DescriptionsButton.prototype.buildCSSClass = function buildCSSClass() { 5429 return 'vjs-descriptions-button ' + _TextTrackButton.prototype.buildCSSClass.call(this); 5430 }; 5431 5432 return DescriptionsButton; 5433}(_textTrackButton2['default']); 5434 5435/** 5436 * `kind` of TextTrack to look for to associate it with this menu. 5437 * 5438 * @type {string} 5439 * @private 5440 */ 5441 5442 5443DescriptionsButton.prototype.kind_ = 'descriptions'; 5444 5445/** 5446 * The text that should display over the `DescriptionsButton`s controls. Added for localization. 5447 * 5448 * @type {string} 5449 * @private 5450 */ 5451DescriptionsButton.prototype.controlText_ = 'Descriptions'; 5452 5453_component2['default'].registerComponent('DescriptionsButton', DescriptionsButton); 5454exports['default'] = DescriptionsButton; 5455 5456},{"30":30,"5":5,"83":83}],28:[function(_dereq_,module,exports){ 5457'use strict'; 5458 5459exports.__esModule = true; 5460 5461var _textTrackMenuItem = _dereq_(31); 5462 5463var _textTrackMenuItem2 = _interopRequireDefault(_textTrackMenuItem); 5464 5465var _component = _dereq_(5); 5466 5467var _component2 = _interopRequireDefault(_component); 5468 5469function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 5470 5471function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 5472 5473function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 5474 5475function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 5476 * @file off-text-track-menu-item.js 5477 */ 5478 5479 5480/** 5481 * A special menu item for turning of a specific type of text track 5482 * 5483 * @extends TextTrackMenuItem 5484 */ 5485var OffTextTrackMenuItem = function (_TextTrackMenuItem) { 5486 _inherits(OffTextTrackMenuItem, _TextTrackMenuItem); 5487 5488 /** 5489 * Creates an instance of this class. 5490 * 5491 * @param {Player} player 5492 * The `Player` that this class should be attached to. 5493 * 5494 * @param {Object} [options] 5495 * The key/value store of player options. 5496 */ 5497 function OffTextTrackMenuItem(player, options) { 5498 _classCallCheck(this, OffTextTrackMenuItem); 5499 5500 // Create pseudo track info 5501 // Requires options['kind'] 5502 options.track = { 5503 player: player, 5504 kind: options.kind, 5505 label: options.kind + ' off', 5506 'default': false, 5507 mode: 'disabled' 5508 }; 5509 5510 // MenuItem is selectable 5511 options.selectable = true; 5512 5513 var _this = _possibleConstructorReturn(this, _TextTrackMenuItem.call(this, player, options)); 5514 5515 _this.selected(true); 5516 return _this; 5517 } 5518 5519 /** 5520 * Handle text track change 5521 * 5522 * @param {EventTarget~Event} event 5523 * The event that caused this function to run 5524 */ 5525 5526 5527 OffTextTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) { 5528 var tracks = this.player().textTracks(); 5529 var selected = true; 5530 5531 for (var i = 0, l = tracks.length; i < l; i++) { 5532 var track = tracks[i]; 5533 5534 if (track.kind === this.track.kind && track.mode === 'showing') { 5535 selected = false;
5536 break; 5537 } 5538 } 5539 5540 this.selected(selected); 5541 }; 5542 5543 return OffTextTrackMenuItem; 5544}(_textTrackMenuItem2['default']); 5545 5546_component2['default'].registerComponent('OffTextTrackMenuItem', OffTextTrackMenuItem); 5547exports['default'] = OffTextTrackMenuItem; 5548 5549},{"31":31,"5":5}],29:[function(_dereq_,module,exports){ 5550'use strict'; 5551 5552exports.__esModule = true; 5553 5554var _textTrackButton = _dereq_(30); 5555 5556var _textTrackButton2 = _interopRequireDefault(_textTrackButton); 5557 5558var _component = _dereq_(5); 5559 5560var _component2 = _interopRequireDefault(_component); 5561 5562function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 5563 5564function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 5565 5566function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 5567 5568function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 5569 * @file subtitles-button.js 5570 */ 5571 5572 5573/** 5574 * The button component for toggling and selecting subtitles 5575 * 5576 * @extends TextTrackButton 5577 */ 5578var SubtitlesButton = function (_TextTrackButton) { 5579 _inherits(SubtitlesButton, _TextTrackButton); 5580 5581 /** 5582 * Creates an instance of this class. 5583 * 5584 * @param {Player} player 5585 * The `Player` that this class should be attached to. 5586 * 5587 * @param {Object} [options] 5588 * The key/value store of player options. 5589 * 5590 * @param {Component~ReadyCallback} [ready] 5591 * The function to call when this component is ready. 5592 */ 5593 function SubtitlesButton(player, options, ready) { 5594 _classCallCheck(this, SubtitlesButton); 5595 5596 var _this = _possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready)); 5597 5598 _this.el_.setAttribute('aria-label', 'Subtitles Menu'); 5599 return _this; 5600 } 5601 5602 /** 5603 * Builds the default DOM `className`. 5604 * 5605 * @return {string} 5606 * The DOM `className` for this object. 5607 */ 5608 5609 5610 SubtitlesButton.prototype.buildCSSClass = function buildCSSClass() { 5611 return 'vjs-subtitles-button ' + _TextTrackButton.prototype.buildCSSClass.call(this); 5612 }; 5613 5614 return SubtitlesButton; 5615}(_textTrackButton2['default']); 5616 5617/** 5618 * `kind` of TextTrack to look for to associate it with this menu. 5619 * 5620 * @type {string} 5621 * @private 5622 */ 5623 5624 5625SubtitlesButton.prototype.kind_ = 'subtitles'; 5626 5627/** 5628 * The text that should display over the `SubtitlesButton`s controls. Added for localization. 5629 * 5630 * @type {string} 5631 * @private 5632 */ 5633SubtitlesButton.prototype.controlText_ = 'Subtitles'; 5634 5635_component2['default'].registerComponent('SubtitlesButton', SubtitlesButton); 5636exports['default'] = SubtitlesButton; 5637 5638},{"30":30,"5":5}],30:[function(_dereq_,module,exports){ 5639'use strict'; 5640 5641exports.__esModule = true; 5642 5643var _trackButton = _dereq_(36); 5644 5645var _trackButton2 = _interopRequireDefault(_trackButton); 5646 5647var _component = _dereq_(5); 5648 5649var _component2 = _interopRequireDefault(_component); 5650 5651var _textTrackMenuItem = _dereq_(31); 5652 5653var _textTrackMenuItem2 = _interopRequireDefault(_textTrackMenuItem); 5654 5655var _offTextTrackMenuItem = _dereq_(28); 5656 5657var _offTextTrackMenuItem2 = _interopRequireDefault(_offTextTrackMenuItem); 5658 5659function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 5660 5661function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 5662 5663function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 5664 5665function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
5665onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 5666 * @file text-track-button.js 5667 */ 5668 5669 5670/** 5671 * The base class for buttons that toggle specific text track types (e.g. subtitles) 5672 * 5673 * @extends MenuButton 5674 */ 5675var TextTrackButton = function (_TrackButton) { 5676 _inherits(TextTrackButton, _TrackButton); 5677 5678 /** 5679 * Creates an instance of this class. 5680 * 5681 * @param {Player} player 5682 * The `Player` that this class should be attached to. 5683 * 5684 * @param {Object} [options={}] 5685 * The key/value store of player options. 5686 */ 5687 function TextTrackButton(player) { 5688 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 5689 5690 _classCallCheck(this, TextTrackButton); 5691 5692 options.tracks = player.textTracks(); 5693 5694 return _possibleConstructorReturn(this, _TrackButton.call(this, player, options)); 5695 } 5696 5697 /** 5698 * Create a menu item for each text track 5699 * 5700 * @param {TextTrackMenuItem[]} [items=[]] 5701 * Existing array of items to use during creation 5702 * 5703 * @return {TextTrackMenuItem[]} 5704 * Array of menu items that were created 5705 */ 5706 5707 5708 TextTrackButton.prototype.createItems = function createItems() { 5709 var items = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; 5710 5711 // Add an OFF menu item to turn all tracks off 5712 items.push(new _offTextTrackMenuItem2['default'](this.player_, { kind: this.kind_ })); 5713 this.hideThreshold_ += 1; 5714 5715 var tracks = this.player_.textTracks(); 5716 5717 if (!tracks) { 5718 return items; 5719 } 5720 5721 for (var i = 0; i < tracks.length; i++) { 5722 var track = tracks[i]; 5723 5724 // only add tracks that are of the appropriate kind and have a label 5725 if (track.kind === this.kind_) { 5726 items.push(new _textTrackMenuItem2['default'](this.player_, { 5727 track: track, 5728 // MenuItem is selectable 5729 selectable: true 5730 })); 5731 } 5732 } 5733 5734 return items; 5735 }; 5736 5737 return TextTrackButton; 5738}(_trackButton2['default']); 5739 5740_component2['default'].registerComponent('TextTrackButton', TextTrackButton); 5741exports['default'] = TextTrackButton; 5742 5743},{"28":28,"31":31,"36":36,"5":5}],31:[function(_dereq_,module,exports){ 5744'use strict'; 5745 5746exports.__esModule = true; 5747 5748var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; 5749 5750var _menuItem = _dereq_(48); 5751 5752var _menuItem2 = _interopRequireDefault(_menuItem); 5753 5754var _component = _dereq_(5); 5755 5756var _component2 = _interopRequireDefault(_component); 5757 5758var _fn = _dereq_(83); 5759 5760var Fn = _interopRequireWildcard(_fn); 5761 5762var _window = _dereq_(95); 5763 5764var _window2 = _interopRequireDefault(_window); 5765 5766var _document = _dereq_(94); 5767 5768var _document2 = _interopRequireDefault(_document); 5769 5770function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 5771 5772function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 5773 5774function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 5775 5776function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 5777 5778function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
5778onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 5779 * @file text-track-menu-item.js 5780 */ 5781 5782 5783/** 5784 * The specific menu item type for selecting a language within a text track kind 5785 * 5786 * @extends MenuItem 5787 */ 5788var TextTrackMenuItem = function (_MenuItem) { 5789 _inherits(TextTrackMenuItem, _MenuItem); 5790 5791 /** 5792 * Creates an instance of this class. 5793 * 5794 * @param {Player} player 5795 * The `Player` that this class should be attached to. 5796 * 5797 * @param {Object} [options] 5798 * The key/value store of player options. 5799 */ 5800 function TextTrackMenuItem(player, options) { 5801 _classCallCheck(this, TextTrackMenuItem); 5802 5803 var track = options.track; 5804 var tracks = player.textTracks(); 5805 5806 // Modify options for parent MenuItem class's init. 5807 options.label = track.label || track.language || 'Unknown'; 5808 options.selected = track['default'] || track.mode === 'showing'; 5809 5810 var _this = _possibleConstructorReturn(this, _MenuItem.call(this, player, options)); 5811 5812 _this.track = track; 5813 5814 if (tracks) { 5815 var changeHandler = Fn.bind(_this, _this.handleTracksChange); 5816 5817 player.on(['loadstart', 'texttrackchange'], changeHandler); 5818 tracks.addEventListener('change', changeHandler); 5819 _this.on('dispose', function () { 5820 tracks.removeEventListener('change', changeHandler); 5821 }); 5822 } 5823 5824 // iOS7 doesn't dispatch change events to TextTrackLists when an 5825 // associated track's mode changes. Without something like 5826 // Object.observe() (also not present on iOS7), it's not 5827 // possible to detect changes to the mode attribute and polyfill 5828 // the change event. As a poor substitute, we manually dispatch 5829 // change events whenever the controls modify the mode. 5830 if (tracks && tracks.onchange === undefined) { 5831 var event = void 0; 5832 5833 _this.on(['tap', 'click'], function () { 5834 if (_typeof(_window2['default'].Event) !== 'object') { 5835 // Android 2.3 throws an Illegal Constructor error for window.Event 5836 try { 5837 event = new _window2['default'].Event('change'); 5838 } catch (err) { 5839 // continue regardless of error 5840 } 5841 } 5842 5843 if (!event) { 5844 event = _document2['default'].createEvent('Event'); 5845 event.initEvent('change', true, true); 5846 } 5847 5848 tracks.dispatchEvent(event); 5849 }); 5850 } 5851 return _this; 5852 } 5853 5854 /** 5855 * This gets called when an `TextTrackMenuItem` is "clicked". See 5856 * {@link ClickableComponent} for more detailed information on what a click can be. 5857 * 5858 * @param {EventTarget~Event} event 5859 * The `keydown`, `tap`, or `click` event that caused this function to be 5860 * called. 5861 * 5862 * @listens tap 5863 * @listens click 5864 */ 5865 5866 5867 TextTrackMenuItem.prototype.handleClick = function handleClick(event) { 5868 var kind = this.track.kind; 5869 var tracks = this.player_.textTracks(); 5870 5871 _MenuItem.prototype.handleClick.call(this, event); 5872 5873 if (!tracks) { 5874 return; 5875 } 5876 5877 for (var i = 0; i < tracks.length; i++) { 5878 var track = tracks[i]; 5879 5880 if (track.kind !== kind) { 5881 continue; 5882 } 5883 5884 if (track === this.track) { 5885 if (track.mode !== 'showing') { 5886 track.mode = 'showing'; 5887 } 5888 } else if (track.mode !== 'disabled') { 5889 track.mode = 'disabled'; 5890 } 5891 } 5892 }; 5893 5894 /** 5895 * Handle text track list change 5896 * 5897 * @param {EventTarget~Event} event 5898 * The `change` event that caused this function to be called. 5899 * 5900 * @listens TextTrackList#change 5901 */ 5902 5903 5904 TextTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) { 5905 this.selected(this.track.mode === 'showing'); 5906 }; 5907 5908 return TextTrackMenuItem; 5909}(_menuItem2['default']); 5910 5911_component2['default'].registerComponent('TextTrackMenuItem', TextTrackMenuItem); 5912exports['default'] = TextTrackMenuItem; 5913 5914},{"48":48,"5":5,"83":83,"94":94,"95":95}],32:[function(_dereq_,module,exports){ 5915'use strict'; 5916 5917exports.__esModule = true; 5918 5919var _component = _dereq_(5); 5920 5921var _component2 = _interopRequireDefault(_component); 5922 5923var _dom = _dereq_(81); 5924 5925var Dom = _interopRequireWildcard(_dom); 5926 5927var _formatTime = _dereq_(84); 5928 5929var _formatTime2 = _interopRequireDefault(_formatTime); 5930 5931function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 5932 5933function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 5934 5935function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 5936 5937function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 5938 5939function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
5939onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 5940 * @file current-time-display.js 5941 */ 5942 5943 5944/** 5945 * Displays the current time 5946 * 5947 * @extends Component 5948 */ 5949var CurrentTimeDisplay = function (_Component) { 5950 _inherits(CurrentTimeDisplay, _Component); 5951 5952 /** 5953 * Creates an instance of this class. 5954 * 5955 * @param {Player} player 5956 * The `Player` that this class should be attached to. 5957 * 5958 * @param {Object} [options] 5959 * The key/value store of player options. 5960 */ 5961 function CurrentTimeDisplay(player, options) { 5962 _classCallCheck(this, CurrentTimeDisplay); 5963 5964 var _this = _possibleConstructorReturn(this, _Component.call(this, player, options)); 5965 5966 _this.on(player, 'timeupdate', _this.updateContent); 5967 return _this; 5968 } 5969 5970 /** 5971 * Create the `Component`'s DOM element 5972 * 5973 * @return {Element} 5974 * The element that was created. 5975 */ 5976 5977 5978 CurrentTimeDisplay.prototype.createEl = function createEl() { 5979 var el = _Component.prototype.createEl.call(this, 'div', { 5980 className: 'vjs-current-time vjs-time-control vjs-control' 5981 }); 5982 5983 this.contentEl_ = Dom.createEl('div', { 5984 className: 'vjs-current-time-display', 5985 // label the current time for screen reader users 5986 innerHTML: '<span class="vjs-control-text">Current Time </span>' + '0:00' 5987 }, { 5988 // tell screen readers not to automatically read the time as it changes 5989 'aria-live': 'off' 5990 }); 5991 5992 el.appendChild(this.contentEl_); 5993 return el; 5994 }; 5995 5996 /** 5997 * Update current time display 5998 * 5999 * @param {EventTarget~Event} [event] 6000 * The `timeupdate` event that caused this function to run. 6001 * 6002 * @listens Player#timeupdate 6003 */ 6004 6005 6006 CurrentTimeDisplay.prototype.updateContent = function updateContent(event) { 6007 // Allows for smooth scrubbing, when player can't keep up. 6008 var time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime(); 6009 var localizedText = this.localize('Current Time'); 6010 var formattedTime = (0, _formatTime2['default'])(time, this.player_.duration()); 6011 6012 if (formattedTime !== this.formattedTime_) { 6013 this.formattedTime_ = formattedTime; 6014 this.contentEl_.innerHTML = '<span class="vjs-control-text">' + localizedText + '</span> ' + formattedTime; 6015 } 6016 }; 6017 6018 return CurrentTimeDisplay; 6019}(_component2['default']); 6020 6021_component2['default'].registerComponent('CurrentTimeDisplay', CurrentTimeDisplay); 6022exports['default'] = CurrentTimeDisplay; 6023 6024},{"5":5,"81":81,"84":84}],33:[function(_dereq_,module,exports){ 6025'use strict'; 6026 6027exports.__esModule = true; 6028 6029var _component = _dereq_(5); 6030 6031var _component2 = _interopRequireDefault(_component); 6032 6033var _dom = _dereq_(81); 6034 6035var Dom = _interopRequireWildcard(_dom); 6036 6037var _formatTime = _dereq_(84); 6038 6039var _formatTime2 = _interopRequireDefault(_formatTime); 6040 6041function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 6042 6043function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 6044 6045function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 6046 6047function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 6048 6049function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
6049onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 6050 * @file duration-display.js 6051 */ 6052 6053 6054/** 6055 * Displays the duration 6056 * 6057 * @extends Component 6058 */ 6059var DurationDisplay = function (_Component) { 6060 _inherits(DurationDisplay, _Component); 6061 6062 /** 6063 * Creates an instance of this class. 6064 * 6065 * @param {Player} player 6066 * The `Player` that this class should be attached to. 6067 * 6068 * @param {Object} [options] 6069 * The key/value store of player options. 6070 */ 6071 function DurationDisplay(player, options) { 6072 _classCallCheck(this, DurationDisplay); 6073 6074 var _this = _possibleConstructorReturn(this, _Component.call(this, player, options)); 6075 6076 _this.on(player, 'durationchange', _this.updateContent); 6077 6078 // Also listen for timeupdate and loadedmetadata because removing those 6079 // listeners could have broken dependent applications/libraries. These 6080 // can likely be removed for 6.0. 6081 _this.on(player, 'timeupdate', _this.updateContent); 6082 _this.on(player, 'loadedmetadata', _this.updateContent); 6083 return _this; 6084 } 6085 6086 /** 6087 * Create the `Component`'s DOM element 6088 * 6089 * @return {Element} 6090 * The element that was created. 6091 */ 6092 6093 6094 DurationDisplay.prototype.createEl = function createEl() { 6095 var el = _Component.prototype.createEl.call(this, 'div', { 6096 className: 'vjs-duration vjs-time-control vjs-control' 6097 }); 6098 6099 this.contentEl_ = Dom.createEl('div', { 6100 className: 'vjs-duration-display', 6101 // label the duration time for screen reader users 6102 innerHTML: '<span class="vjs-control-text">' + this.localize('Duration Time') + '</span> 0:00' 6103 }, { 6104 // tell screen readers not to automatically read the time as it changes 6105 'aria-live': 'off' 6106 }); 6107 6108 el.appendChild(this.contentEl_); 6109 return el; 6110 }; 6111 6112 /** 6113 * Update duration time display. 6114 * 6115 * @param {EventTarget~Event} [event] 6116 * The `durationchange`, `timeupdate`, or `loadedmetadata` event that caused 6117 * this function to be called. 6118 * 6119 * @listens Player#durationchange 6120 * @listens Player#timeupdate 6121 * @listens Player#loadedmetadata 6122 */ 6123 6124 6125 DurationDisplay.prototype.updateContent = function updateContent(event) { 6126 var duration = this.player_.duration(); 6127 6128 if (duration && this.duration_ !== duration) { 6129 this.duration_ = duration; 6130 var localizedText = this.localize('Duration Time'); 6131 var formattedTime = (0, _formatTime2['default'])(duration); 6132 6133 // label the duration time for screen reader users 6134 this.contentEl_.innerHTML = '<span class="vjs-control-text">' + localizedText + '</span> ' + formattedTime; 6135 } 6136 }; 6137 6138 return DurationDisplay; 6139}(_component2['default']); 6140 6141_component2['default'].registerComponent('DurationDisplay', DurationDisplay); 6142exports['default'] = DurationDisplay; 6143 6144},{"5":5,"81":81,"84":84}],34:[function(_dereq_,module,exports){ 6145'use strict'; 6146 6147exports.__esModule = true; 6148 6149var _component = _dereq_(5); 6150 6151var _component2 = _interopRequireDefault(_component); 6152 6153var _dom = _dereq_(81); 6154 6155var Dom = _interopRequireWildcard(_dom); 6156 6157var _formatTime = _dereq_(84); 6158 6159var _formatTime2 = _interopRequireDefault(_formatTime); 6160 6161function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 6162 6163function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 6164 6165function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 6166 6167function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 6168 6169function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
6169onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 6170 * @file remaining-time-display.js 6171 */ 6172 6173 6174/** 6175 * Displays the time left in the video 6176 * 6177 * @extends Component 6178 */ 6179var RemainingTimeDisplay = function (_Component) { 6180 _inherits(RemainingTimeDisplay, _Component); 6181 6182 /** 6183 * Creates an instance of this class. 6184 * 6185 * @param {Player} player 6186 * The `Player` that this class should be attached to. 6187 * 6188 * @param {Object} [options] 6189 * The key/value store of player options. 6190 */ 6191 function RemainingTimeDisplay(player, options) { 6192 _classCallCheck(this, RemainingTimeDisplay); 6193 6194 var _this = _possibleConstructorReturn(this, _Component.call(this, player, options)); 6195 6196 _this.on(player, 'timeupdate', _this.updateContent); 6197 _this.on(player, 'durationchange', _this.updateContent); 6198 return _this; 6199 } 6200 6201 /** 6202 * Create the `Component`'s DOM element 6203 * 6204 * @return {Element} 6205 * The element that was created. 6206 */ 6207 6208 6209 RemainingTimeDisplay.prototype.createEl = function createEl() { 6210 var el = _Component.prototype.createEl.call(this, 'div', { 6211 className: 'vjs-remaining-time vjs-time-control vjs-control' 6212 }); 6213 6214 this.contentEl_ = Dom.createEl('div', { 6215 className: 'vjs-remaining-time-display', 6216 // label the remaining time for screen reader users 6217 innerHTML: '<span class="vjs-control-text">' + this.localize('Remaining Time') + '</span> -0:00' 6218 }, { 6219 // tell screen readers not to automatically read the time as it changes 6220 'aria-live': 'off' 6221 }); 6222 6223 el.appendChild(this.contentEl_); 6224 return el; 6225 }; 6226 6227 /** 6228 * Update remaining time display. 6229 * 6230 * @param {EventTarget~Event} [event] 6231 * The `timeupdate` or `durationchange` event that caused this to run. 6232 * 6233 * @listens Player#timeupdate 6234 * @listens Player#durationchange 6235 */ 6236 6237 6238 RemainingTimeDisplay.prototype.updateContent = function updateContent(event) { 6239 if (this.player_.duration()) { 6240 var localizedText = this.localize('Remaining Time'); 6241 var formattedTime = (0, _formatTime2['default'])(this.player_.remainingTime()); 6242 6243 if (formattedTime !== this.formattedTime_) { 6244 this.formattedTime_ = formattedTime; 6245 this.contentEl_.innerHTML = '<span class="vjs-control-text">' + localizedText + '</span> -' + formattedTime; 6246 } 6247 } 6248 6249 // Allows for smooth scrubbing, when player can't keep up. 6250 // var time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime(); 6251 // this.contentEl_.innerHTML = vjs.formatTime(time, this.player_.duration()); 6252 }; 6253 6254 return RemainingTimeDisplay; 6255}(_component2['default']); 6256 6257_component2['default'].registerComponent('RemainingTimeDisplay', RemainingTimeDisplay); 6258exports['default'] = RemainingTimeDisplay; 6259 6260},{"5":5,"81":81,"84":84}],35:[function(_dereq_,module,exports){ 6261'use strict'; 6262 6263exports.__esModule = true; 6264 6265var _component = _dereq_(5); 6266 6267var _component2 = _interopRequireDefault(_component); 6268 6269function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 6270 6271function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 6272 6273function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 6274 6275function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
6275onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 6276 * @file time-divider.js 6277 */ 6278 6279 6280/** 6281 * The separator between the current time and duration. 6282 * Can be hidden if it's not needed in the design. 6283 * 6284 * @extends Component 6285 */ 6286var TimeDivider = function (_Component) { 6287 _inherits(TimeDivider, _Component); 6288 6289 function TimeDivider() { 6290 _classCallCheck(this, TimeDivider); 6291 6292 return _possibleConstructorReturn(this, _Component.apply(this, arguments)); 6293 } 6294 6295 /** 6296 * Create the component's DOM element 6297 * 6298 * @return {Element} 6299 * The element that was created. 6300 */ 6301 TimeDivider.prototype.createEl = function createEl() { 6302 return _Component.prototype.createEl.call(this, 'div', { 6303 className: 'vjs-time-control vjs-time-divider', 6304 innerHTML: '<div><span>/</span></div>' 6305 }); 6306 }; 6307 6308 return TimeDivider; 6309}(_component2['default']); 6310 6311_component2['default'].registerComponent('TimeDivider', TimeDivider); 6312exports['default'] = TimeDivider; 6313 6314},{"5":5}],36:[function(_dereq_,module,exports){ 6315'use strict'; 6316 6317exports.__esModule = true; 6318 6319var _menuButton = _dereq_(47); 6320 6321var _menuButton2 = _interopRequireDefault(_menuButton); 6322 6323var _component = _dereq_(5); 6324 6325var _component2 = _interopRequireDefault(_component); 6326 6327var _fn = _dereq_(83); 6328 6329var Fn = _interopRequireWildcard(_fn); 6330 6331function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 6332 6333function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 6334 6335function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 6336 6337function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 6338 6339function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 6340 * @file track-button.js 6341 */ 6342 6343 6344/** 6345 * The base class for buttons that toggle specific track types (e.g. subtitles). 6346 * 6347 * @extends MenuButton 6348 */ 6349var TrackButton = function (_MenuButton) { 6350 _inherits(TrackButton, _MenuButton); 6351 6352 /** 6353 * Creates an instance of this class. 6354 * 6355 * @param {Player} player 6356 * The `Player` that this class should be attached to. 6357 * 6358 * @param {Object} [options] 6359 * The key/value store of player options. 6360 */ 6361 function TrackButton(player, options) { 6362 _classCallCheck(this, TrackButton); 6363 6364 var tracks = options.tracks; 6365 6366 var _this = _possibleConstructorReturn(this, _MenuButton.call(this, player, options)); 6367 6368 if (_this.items.length <= 1) { 6369 _this.hide(); 6370 } 6371 6372 if (!tracks) { 6373 return _possibleConstructorReturn(_this); 6374 } 6375 6376 var updateHandler = Fn.bind(_this, _this.update); 6377 6378 tracks.addEventListener('removetrack', updateHandler); 6379 tracks.addEventListener('addtrack', updateHandler); 6380 6381 _this.player_.on('dispose', function () { 6382 tracks.removeEventListener('removetrack', updateHandler); 6383 tracks.removeEventListener('addtrack', updateHandler); 6384 }); 6385 return _this; 6386 } 6387 6388 return TrackButton; 6389}(_menuButton2['default']); 6390 6391_component2['default'].registerComponent('TrackButton', TrackButton); 6392exports['default'] = TrackButton; 6393 6394},{"47":47,"5":5,"83":83}],37:[function(_dereq_,module,exports){ 6395'use strict'; 6396 6397exports.__esModule = true; 6398 6399var _slider = _dereq_(57); 6400 6401var _slider2 = _interopRequireDefault(_slider); 6402 6403var _component = _dereq_(5); 6404 6405var _component2 = _interopRequireDefault(_component); 6406 6407var _fn = _dereq_(83); 6408 6409var Fn = _interopRequireWildcard(_fn); 6410 6411_dereq_(39); 6412 6413function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 6414 6415function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 6416 6417function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 6418 6419function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 6420 6421function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
6421onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 6422 * @file volume-bar.js 6423 */ 6424 6425 6426// Required children 6427 6428 6429/** 6430 * The bar that contains the volume level and can be clicked on to adjust the level 6431 * 6432 * @extends Slider 6433 */ 6434var VolumeBar = function (_Slider) { 6435 _inherits(VolumeBar, _Slider); 6436 6437 /** 6438 * Creates an instance of this class. 6439 * 6440 * @param {Player} player 6441 * The `Player` that this class should be attached to. 6442 * 6443 * @param {Object} [options] 6444 * The key/value store of player options. 6445 */ 6446 function VolumeBar(player, options) { 6447 _classCallCheck(this, VolumeBar); 6448 6449 var _this = _possibleConstructorReturn(this, _Slider.call(this, player, options)); 6450 6451 _this.on(player, 'volumechange', _this.updateARIAAttributes); 6452 player.ready(Fn.bind(_this, _this.updateARIAAttributes)); 6453 return _this; 6454 } 6455 6456 /** 6457 * Create the `Component`'s DOM element 6458 * 6459 * @return {Element} 6460 * The element that was created. 6461 */ 6462 6463 6464 VolumeBar.prototype.createEl = function createEl() { 6465 return _Slider.prototype.createEl.call(this, 'div', { 6466 className: 'vjs-volume-bar vjs-slider-bar' 6467 }, { 6468 'aria-label': 'volume level' 6469 }); 6470 }; 6471 6472 /** 6473 * Handle movement events on the {@link VolumeMenuButton}. 6474 * 6475 * @param {EventTarget~Event} event 6476 * The event that caused this function to run. 6477 * 6478 * @listens mousemove 6479 */ 6480 6481 6482 VolumeBar.prototype.handleMouseMove = function handleMouseMove(event) { 6483 this.checkMuted(); 6484 this.player_.volume(this.calculateDistance(event)); 6485 }; 6486 6487 /** 6488 * If the player is muted unmute it. 6489 */ 6490 6491 6492 VolumeBar.prototype.checkMuted = function checkMuted() { 6493 if (this.player_.muted()) { 6494 this.player_.muted(false); 6495 } 6496 }; 6497 6498 /** 6499 * Get percent of volume level 6500 * 6501 * @return {number} 6502 * Volume level percent as a decimal number. 6503 */ 6504 6505 6506 VolumeBar.prototype.getPercent = function getPercent() { 6507 if (this.player_.muted()) { 6508 return 0; 6509 } 6510 return this.player_.volume(); 6511 }; 6512 6513 /** 6514 * Increase volume level for keyboard users 6515 */ 6516 6517 6518 VolumeBar.prototype.stepForward = function stepForward() { 6519 this.checkMuted(); 6520 this.player_.volume(this.player_.volume() + 0.1); 6521 }; 6522 6523 /** 6524 * Decrease volume level for keyboard users 6525 */ 6526 6527 6528 VolumeBar.prototype.stepBack = function stepBack() { 6529 this.checkMuted(); 6530 this.player_.volume(this.player_.volume() - 0.1); 6531 }; 6532 6533 /** 6534 * Update ARIA accessibility attributes 6535 * 6536 * @param {EventTarget~Event} [event] 6537 * The `volumechange` event that caused this function to run. 6538 * 6539 * @listens Player#volumechange 6540 */ 6541 6542 6543 VolumeBar.prototype.updateARIAAttributes = function updateARIAAttributes(event) { 6544 // Current value of volume bar as a percentage 6545 var volume = (this.player_.volume() * 100).toFixed(2); 6546 6547 this.el_.setAttribute('aria-valuenow', volume); 6548 this.el_.setAttribute('aria-valuetext', volume + '%'); 6549 }; 6550 6551 return VolumeBar; 6552}(_slider2['default']); 6553 6554/** 6555 * Default options for the `VolumeBar` 6556 * 6557 * @type {Object} 6558 * @private 6559 */ 6560 6561 6562VolumeBar.prototype.options_ = { 6563 children: ['volumeLevel'], 6564 barName: 'volumeLevel' 6565}; 6566 6567/** 6568 * Call the update event for this Slider when this event happens on the player. 6569 * 6570 * @type {string} 6571 */ 6572VolumeBar.prototype.playerEvent = 'volumechange'; 6573 6574_component2['default'].registerComponent('VolumeBar', VolumeBar); 6575exports['default'] = VolumeBar; 6576 6577},{"39":39,"5":5,"57":57,"83":83}],38:[function(_dereq_,module,exports){ 6578'use strict'; 6579 6580exports.__esModule = true; 6581 6582var _component = _dereq_(5); 6583 6584var _component2 = _interopRequireDefault(_component); 6585 6586_dereq_(37); 6587 6588function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 6589 6590function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 6591 6592function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 6593 6594function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
6594onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 6595 * @file volume-control.js 6596 */ 6597 6598 6599// Required children 6600 6601 6602/** 6603 * The component for controlling the volume level 6604 * 6605 * @extends Component 6606 */ 6607var VolumeControl = function (_Component) { 6608 _inherits(VolumeControl, _Component); 6609 6610 /** 6611 * Creates an instance of this class. 6612 * 6613 * @param {Player} player 6614 * The `Player` that this class should be attached to. 6615 * 6616 * @param {Object} [options={}] 6617 * The key/value store of player options. 6618 */ 6619 function VolumeControl(player, options) { 6620 _classCallCheck(this, VolumeControl); 6621 6622 // hide volume controls when they're not supported by the current tech 6623 var _this = _possibleConstructorReturn(this, _Component.call(this, player, options)); 6624 6625 if (player.tech_ && player.tech_.featuresVolumeControl === false) { 6626 _this.addClass('vjs-hidden'); 6627 } 6628 _this.on(player, 'loadstart', function () { 6629 if (player.tech_.featuresVolumeControl === false) { 6630 this.addClass('vjs-hidden'); 6631 } else { 6632 this.removeClass('vjs-hidden'); 6633 } 6634 }); 6635 return _this; 6636 } 6637 6638 /** 6639 * Create the `Component`'s DOM element 6640 * 6641 * @return {Element} 6642 * The element that was created. 6643 */ 6644 6645 6646 VolumeControl.prototype.createEl = function createEl() { 6647 return _Component.prototype.createEl.call(this, 'div', { 6648 className: 'vjs-volume-control vjs-control' 6649 }); 6650 }; 6651 6652 return VolumeControl; 6653}(_component2['default']); 6654 6655/** 6656 * Default options for the `VolumeControl` 6657 * 6658 * @type {Object} 6659 * @private 6660 */ 6661 6662 6663VolumeControl.prototype.options_ = { 6664 children: ['volumeBar'] 6665}; 6666 6667_component2['default'].registerComponent('VolumeControl', VolumeControl); 6668exports['default'] = VolumeControl; 6669 6670},{"37":37,"5":5}],39:[function(_dereq_,module,exports){ 6671'use strict'; 6672 6673exports.__esModule = true; 6674 6675var _component = _dereq_(5); 6676 6677var _component2 = _interopRequireDefault(_component); 6678 6679function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 6680 6681function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 6682 6683function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 6684 6685function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 6686 * @file volume-level.js 6687 */ 6688 6689 6690/** 6691 * Shows volume level 6692 * 6693 * @extends Component 6694 */ 6695var VolumeLevel = function (_Component) { 6696 _inherits(VolumeLevel, _Component); 6697 6698 function VolumeLevel() { 6699 _classCallCheck(this, VolumeLevel); 6700 6701 return _possibleConstructorReturn(this, _Component.apply(this, arguments)); 6702 } 6703 6704 /** 6705 * Create the `Component`'s DOM element 6706 * 6707 * @return {Element} 6708 * The element that was created. 6709 */ 6710 VolumeLevel.prototype.createEl = function createEl() { 6711 return _Component.prototype.createEl.call(this, 'div', { 6712 className: 'vjs-volume-level', 6713 innerHTML: '<span class="vjs-control-text"></span>' 6714 }); 6715 }; 6716 6717 return VolumeLevel; 6718}(_component2['default']); 6719 6720_component2['default'].registerComponent('VolumeLevel', VolumeLevel); 6721exports['default'] = VolumeLevel; 6722 6723},{"5":5}],40:[function(_dereq_,module,exports){ 6724'use strict'; 6725 6726exports.__esModule = true; 6727 6728var _fn = _dereq_(83); 6729 6730var Fn = _interopRequireWildcard(_fn); 6731 6732var _component = _dereq_(5); 6733 6734var _component2 = _interopRequireDefault(_component); 6735 6736var _popup = _dereq_(54); 6737 6738var _popup2 = _interopRequireDefault(_popup); 6739 6740var _popupButton = _dereq_(53); 6741 6742var _popupButton2 = _interopRequireDefault(_popupButton); 6743 6744var _muteToggle = _dereq_(11); 6745 6746var _muteToggle2 = _interopRequireDefault(_muteToggle); 6747 6748var _volumeBar = _dereq_(37); 6749 6750var _volumeBar2 = _interopRequireDefault(_volumeBar); 6751 6752function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 6753 6754function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 6755 6756function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 6757 6758function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 6759 6760function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
6760onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 6761 * @file volume-menu-button.js 6762 */ 6763 6764 6765/** 6766 * Button for volume popup 6767 * 6768 * @extends PopupButton 6769 */ 6770var VolumeMenuButton = function (_PopupButton) { 6771 _inherits(VolumeMenuButton, _PopupButton); 6772 6773 /** 6774 * Creates an instance of this class. 6775 * 6776 * @param {Player} player 6777 * The `Player` that this class should be attached to. 6778 * 6779 * @param {Object} [options={}] 6780 * The key/value store of player options. 6781 */ 6782 function VolumeMenuButton(player) { 6783 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 6784 6785 _classCallCheck(this, VolumeMenuButton); 6786 6787 // Default to inline 6788 if (options.inline === undefined) { 6789 options.inline = true; 6790 } 6791 6792 // If the vertical option isn't passed at all, default to true. 6793 if (options.vertical === undefined) { 6794 // If an inline volumeMenuButton is used, we should default to using 6795 // a horizontal slider for obvious reasons. 6796 if (options.inline) { 6797 options.vertical = false;
6798 } else { 6799 options.vertical = true; 6800 } 6801 } 6802 6803 // The vertical option needs to be set on the volumeBar as well, 6804 // since that will need to be passed along to the VolumeBar constructor 6805 options.volumeBar = options.volumeBar || {}; 6806 options.volumeBar.vertical = !!options.vertical; 6807 6808 // Same listeners as MuteToggle 6809 var _this = _possibleConstructorReturn(this, _PopupButton.call(this, player, options)); 6810 6811 _this.on(player, 'volumechange', _this.volumeUpdate); 6812 _this.on(player, 'loadstart', _this.volumeUpdate); 6813 6814 // hide mute toggle if the current tech doesn't support volume control 6815 function updateVisibility() { 6816 if (player.tech_ && player.tech_.featuresVolumeControl === false) { 6817 this.addClass('vjs-hidden'); 6818 } else { 6819 this.removeClass('vjs-hidden'); 6820 } 6821 } 6822 6823 updateVisibility.call(_this); 6824 _this.on(player, 'loadstart', updateVisibility); 6825 6826 _this.on(_this.volumeBar, ['slideractive', 'focus'], function () { 6827 this.addClass('vjs-slider-active'); 6828 }); 6829 6830 _this.on(_this.volumeBar, ['sliderinactive', 'blur'], function () { 6831 this.removeClass('vjs-slider-active'); 6832 }); 6833 6834 _this.on(_this.volumeBar, ['focus'], function () { 6835 this.addClass('vjs-lock-showing'); 6836 }); 6837 6838 _this.on(_this.volumeBar, ['blur'], function () { 6839 this.removeClass('vjs-lock-showing'); 6840 }); 6841 return _this; 6842 } 6843 6844 /** 6845 * Builds the default DOM `className`. 6846 * 6847 * @return {string} 6848 * The DOM `className` for this object. 6849 */ 6850 6851 6852 VolumeMenuButton.prototype.buildCSSClass = function buildCSSClass() { 6853 var orientationClass = ''; 6854 6855 if (this.options_.vertical) { 6856 orientationClass = 'vjs-volume-menu-button-vertical'; 6857 } else { 6858 orientationClass = 'vjs-volume-menu-button-horizontal'; 6859 } 6860 6861 return 'vjs-volume-menu-button ' + _PopupButton.prototype.buildCSSClass.call(this) + ' ' + orientationClass; 6862 }; 6863 6864 /** 6865 * Create the VolumeMenuButton popup 6866 * 6867 * @return {Popup} 6868 * The popup that was created 6869 */ 6870 6871 6872 VolumeMenuButton.prototype.createPopup = function createPopup() { 6873 var popup = new _popup2['default'](this.player_, { 6874 contentElType: 'div' 6875 }); 6876 6877 var vb = new _volumeBar2['default'](this.player_, this.options_.volumeBar); 6878 6879 popup.addChild(vb); 6880 6881 this.menuContent = popup; 6882 this.volumeBar = vb; 6883 6884 this.attachVolumeBarEvents(); 6885 6886 return popup; 6887 }; 6888 6889 /** 6890 * This gets called when an `VolumeMenuButton` is "clicked". See 6891 * {@link ClickableComponent} for more detailed information on what a click can be. 6892 * 6893 * @param {EventTarget~Event} [event] 6894 * The `keydown`, `tap`, or `click` event that caused this function to be 6895 * called. 6896 * 6897 * @listens tap 6898 * @listens click 6899 */ 6900 6901 6902 VolumeMenuButton.prototype.handleClick = function handleClick(event) { 6903 _muteToggle2['default'].prototype.handleClick.call(this); 6904 _PopupButton.prototype.handleClick.call(this); 6905 }; 6906 6907 /** 6908 * Add events listeners to the created `VolumeBar`. 6909 */ 6910 6911 6912 VolumeMenuButton.prototype.attachVolumeBarEvents = function attachVolumeBarEvents() { 6913 this.menuContent.on(['mousedown', 'touchdown'], Fn.bind(this, this.handleMouseDown)); 6914 }; 6915 6916 /** 6917 * Handle the `mousedown` and `touchdown` events on the `VolumeBar` 6918 * 6919 * @param {EventTarget~Event} [event] 6920 * The `mousedown` or `touchdown` event that caused this to run. 6921 * 6922 * @listens mousedown 6923 * @listens touchdown 6924 */ 6925 6926 6927 VolumeMenuButton.prototype.handleMouseDown = function handleMouseDown(event) { 6928 this.on(['mousemove', 'touchmove'], Fn.bind(this.volumeBar, this.volumeBar.handleMouseMove)); 6929 this.on(this.el_.ownerDocument, ['mouseup', 'touchend'], this.handleMouseUp); 6930 }; 6931 6932 /** 6933 * Handle the `mouseup` and `touchend` events on the `VolumeBar` 6934 * 6935 * @param {EventTarget~Event} [event] 6936 * The `mouseup` or `touchend` event that caused this to run. 6937 * 6938 * @listens mouseup 6939 * @listens touchend 6940 */ 6941 6942 6943 VolumeMenuButton.prototype.handleMouseUp = function handleMouseUp(event) { 6944 this.off(['mousemove', 'touchmove'], Fn.bind(this.volumeBar, this.volumeBar.handleMouseMove)); 6945 }; 6946 6947 return VolumeMenuButton; 6948}(_popupButton2['default']); 6949 6950/** 6951 * @borrows MuteToggle#update as VolumeMenuButton#volumeUpdate 6952 */ 6953 6954 6955VolumeMenuButton.prototype.volumeUpdate = _muteToggle2['default'].prototype.update; 6956 6957/** 6958 * The text that should display over the `VolumeMenuButton`s controls. Added for localization. 6959 * 6960 * @type {string} 6961 * @private 6962 */ 6963VolumeMenuButton.prototype.controlText_ = 'Mute'; 6964 6965_component2['default'].registerComponent('VolumeMenuButton', VolumeMenuButton); 6966exports['default'] = VolumeMenuButton; 6967 6968},{"11":11,"37":37,"5":5,"53":53,"54":54,"83":83}],41:[function(_dereq_,module,exports){ 6969'use strict'; 6970 6971exports.__esModule = true; 6972 6973var _component = _dereq_(5); 6974 6975var _component2 = _interopRequireDefault(_component); 6976 6977var _modalDialog = _dereq_(50); 6978 6979var _modalDialog2 = _interopRequireDefault(_modalDialog); 6980 6981var _mergeOptions = _dereq_(87); 6982 6983var _mergeOptions2 = _interopRequireDefault(_mergeOptions); 6984 6985function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 6986 6987function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 6988 6989function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 6990 6991function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
6991onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 6992 * @file error-display.js 6993 */ 6994 6995 6996/** 6997 * A display that indicates an error has occurred. This means that the video 6998 * is unplayable. 6999 * 7000 * @extends ModalDialog 7001 */ 7002var ErrorDisplay = function (_ModalDialog) { 7003 _inherits(ErrorDisplay, _ModalDialog); 7004 7005 /** 7006 * Creates an instance of this class. 7007 * 7008 * @param {Player} player 7009 * The `Player` that this class should be attached to. 7010 * 7011 * @param {Object} [options] 7012 * The key/value store of player options. 7013 */ 7014 function ErrorDisplay(player, options) { 7015 _classCallCheck(this, ErrorDisplay); 7016 7017 var _this = _possibleConstructorReturn(this, _ModalDialog.call(this, player, options)); 7018 7019 _this.on(player, 'error', _this.open); 7020 return _this; 7021 } 7022 7023 /** 7024 * Builds the default DOM `className`. 7025 * 7026 * @return {string} 7027 * The DOM `className` for this object. 7028 * 7029 * @deprecated Since version 5. 7030 */ 7031 7032 7033 ErrorDisplay.prototype.buildCSSClass = function buildCSSClass() { 7034 return 'vjs-error-display ' + _ModalDialog.prototype.buildCSSClass.call(this); 7035 }; 7036 7037 /** 7038 * Gets the localized error message based on the `Player`s error. 7039 * 7040 * @return {string} 7041 * The `Player`s error message localized or an empty string. 7042 */ 7043 7044 7045 ErrorDisplay.prototype.content = function content() { 7046 var error = this.player().error(); 7047 7048 return error ? this.localize(error.message) : ''; 7049 }; 7050 7051 return ErrorDisplay; 7052}(_modalDialog2['default']); 7053 7054/** 7055 * The default options for an `ErrorDisplay`. 7056 * 7057 * @private 7058 */ 7059 7060 7061ErrorDisplay.prototype.options_ = (0, _mergeOptions2['default'])(_modalDialog2['default'].prototype.options_, { 7062 pauseOnOpen: false, 7063 fillAlways: true, 7064 temporary: false, 7065 uncloseable: true 7066}); 7067 7068_component2['default'].registerComponent('ErrorDisplay', ErrorDisplay); 7069exports['default'] = ErrorDisplay; 7070 7071},{"5":5,"50":50,"87":87}],42:[function(_dereq_,module,exports){ 7072'use strict'; 7073 7074exports.__esModule = true; 7075 7076var _events = _dereq_(82); 7077 7078var Events = _interopRequireWildcard(_events); 7079 7080function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 7081 7082/** 7083 * `EventTarget` is a class that can have the same API as the DOM `EventTarget`. It 7084 * adds shorthand functions that wrap around lengthy functions. For example: 7085 * the `on` function is a wrapper around `addEventListener`. 7086 * 7087 * @see [EventTarget Spec]{@link https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget} 7088 * @class EventTarget 7089 */ 7090var EventTarget = function EventTarget() {}; 7091 7092/** 7093 * A Custom DOM event. 7094 * 7095 * @typedef {Object} EventTarget~Event 7096 * @see [Properties]{@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent} 7097 */ 7098 7099/** 7100 * All event listeners should follow the following format. 7101 * 7102 * @callback EventTarget~EventListener 7103 * @this {EventTarget} 7104 * 7105 * @param {EventTarget~Event} event 7106 * the event that triggered this function 7107 * 7108 * @param {Object} [hash] 7109 * hash of data sent during the event 7110 */ 7111 7112/**
7113 * An object containing event names as keys and booleans as values. 7114 * 7115 * > NOTE: If an event name is set to a true value here {@link EventTarget#trigger} 7116 * will have extra functionality. See that function for more information. 7117 * 7118 * @property EventTarget.prototype.allowedEvents_ 7119 * @private 7120 */ 7121/** 7122 * @file src/js/event-target.js 7123 */ 7124EventTarget.prototype.allowedEvents_ = {}; 7125 7126/** 7127 * Adds an `event listener` to an instance of an `EventTarget`. An `event listener` is a 7128 * function that will get called when an event with a certain name gets triggered. 7129 * 7130 * @param {string|string[]} type 7131 * An event name or an array of event names. 7132 * 7133 * @param {EventTarget~EventListener} fn 7134 * The function to call with `EventTarget`s 7135 */ 7136EventTarget.prototype.on = function (type, fn) { 7137 // Remove the addEventListener alias before calling Events.on 7138 // so we don't get into an infinite type loop 7139 var ael = this.addEventListener; 7140 7141 this.addEventListener = function () {}; 7142 Events.on(this, type, fn); 7143 this.addEventListener = ael; 7144}; 7145 7146/** 7147 * An alias of {@link EventTarget#on}. Allows `EventTarget` to mimic 7148 * the standard DOM API. 7149 * 7150 * @function 7151 * @see {@link EventTarget#on} 7152 */ 7153EventTarget.prototype.addEventListener = EventTarget.prototype.on; 7154 7155/** 7156 * Removes an `event listener` for a specific event from an instance of `EventTarget`. 7157 * This makes it so that the `event listener` will no longer get called when the 7158 * named event happens. 7159 * 7160 * @param {string|string[]} type 7161 * An event name or an array of event names. 7162 * 7163 * @param {EventTarget~EventListener} fn 7164 * The function to remove. 7165 */ 7166EventTarget.prototype.off = function (type, fn) { 7167 Events.off(this, type, fn); 7168}; 7169 7170/** 7171 * An alias of {@link EventTarget#off}. Allows `EventTarget` to mimic 7172 * the standard DOM API. 7173 * 7174 * @function 7175 * @see {@link EventTarget#off} 7176 */ 7177EventTarget.prototype.removeEventListener = EventTarget.prototype.off; 7178 7179/** 7180 * This function will add an `event listener` that gets triggered only once. After the 7181 * first trigger it will get removed. This is like adding an `event listener` 7182 * with {@link EventTarget#on} that calls {@link EventTarget#off} on itself. 7183 * 7184 * @param {string|string[]} type 7185 * An event name or an array of event names. 7186 * 7187 * @param {EventTarget~EventListener} fn 7188 * The function to be called once for each event name. 7189 */ 7190EventTarget.prototype.one = function (type, fn) { 7191 // Remove the addEventListener alialing Events.on 7192 // so we don't get into an infinite type loop 7193 var ael = this.addEventListener; 7194 7195 this.addEventListener = function () {}; 7196 Events.one(this, type, fn); 7197 this.addEventListener = ael; 7198}; 7199 7200/** 7201 * This function causes an event to happen. This will then cause any `event listeners` 7202 * that are waiting for that event, to get called. If there are no `event listeners` 7203 * for an event then nothing will happen. 7204 * 7205 * If the name of the `Event` that is being triggered is in `EventTarget.allowedEvents_`. 7206 * Trigger will also call the `on` + `uppercaseEventName` function. 7207 * 7208 * Example: 7209 * 'click' is in `EventTarget.allowedEvents_`, so, trigger will attempt to call 7210 * `onClick` if it exists. 7211 * 7212 * @param {string|EventTarget~Event|Object} event 7213 * The name of the event, an `Event`, or an object with a key of type set to 7214 * an event name. 7215 */ 7216EventTarget.prototype.trigger = function (event) { 7217 var type = event.type || event; 7218 7219 if (typeof event === 'string') { 7220 event = { type: type }; 7221 } 7222 event = Events.fixEvent(event); 7223 7224 if (this.allowedEvents_[type] && this['on' + type]) { 7225 this['on' + type](event); 7226 } 7227 7228 Events.trigger(this, event); 7229}; 7230 7231/** 7232 * An alias of {@link EventTarget#trigger}. Allows `EventTarget` to mimic 7233 * the standard DOM API. 7234 * 7235 * @function 7236 * @see {@link EventTarget#trigger} 7237 */ 7238EventTarget.prototype.dispatchEvent = EventTarget.prototype.trigger; 7239 7240exports['default'] = EventTarget; 7241 7242},{"82":82}],43:[function(_dereq_,module,exports){ 7243'use strict'; 7244 7245exports.__esModule = true; 7246 7247var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; 7248 7249var _log = _dereq_(86); 7250 7251var _log2 = _interopRequireDefault(_log); 7252 7253var _obj = _dereq_(88); 7254 7255function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 7256 7257/** 7258 * @file extend.js 7259 * @module extend 7260 */ 7261
7262/** 7263 * A combination of node inherits and babel's inherits (after transpile). 7264 * Both work the same but node adds `super_` to the subClass 7265 * and Bable adds the superClass as __proto__. Both seem useful. 7266 * 7267 * @param {Object} subClass 7268 * The class to inherit to 7269 * 7270 * @param {Object} superClass 7271 * The class to inherit from 7272 * 7273 * @private 7274 */ 7275var _inherits = function _inherits(subClass, superClass) { 7276 if (typeof superClass !== 'function' && superClass !== null) { 7277 throw new TypeError('Super expression must either be null or a function, not ' + (typeof superClass === 'undefined' ? 'undefined' : _typeof(superClass))); 7278 } 7279 7280 subClass.prototype = Object.create(superClass && superClass.prototype, { 7281 constructor: { 7282 value: subClass, 7283 enumerable: false, 7284 writable: true, 7285 configurable: true 7286 } 7287 }); 7288 7289 if (superClass) { 7290 // node 7291 subClass.super_ = superClass; 7292 } 7293}; 7294 7295/** 7296 * Function for subclassing using the same inheritance that 7297 * videojs uses internally 7298 * 7299 * @param {Object} superClass 7300 * The class to inherit from 7301 * 7302 * @param {Object} [subClassMethods={}] 7303 * The class to inherit to 7304 * 7305 * @return {Object} 7306 * The new object with subClassMethods that inherited superClass. 7307 */ 7308var extendFn = function extendFn(superClass) { 7309 var subClassMethods = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 7310 7311 var subClass = function subClass() { 7312 superClass.apply(this, arguments); 7313 }; 7314 7315 var methods = {}; 7316 7317 if ((0, _obj.isObject)(subClassMethods)) { 7318 if (typeof subClassMethods.init === 'function') { 7319 _log2['default'].warn('Constructor logic via init() is deprecated; please use constructor() instead.'); 7320 subClassMethods.constructor = subClassMethods.init; 7321 } 7322 if (subClassMethods.constructor !== Object.prototype.constructor) { 7323 subClass = subClassMethods.constructor; 7324 } 7325 methods = subClassMethods; 7326 } else if (typeof subClassMethods === 'function') { 7327 subClass = subClassMethods; 7328 } 7329 7330 _inherits(subClass, superClass); 7331 7332 // Extend subObj's prototype with functions and other properties from props 7333 for (var name in methods) { 7334 if (methods.hasOwnProperty(name)) { 7335 subClass.prototype[name] = methods[name]; 7336 } 7337 } 7338 7339 return subClass; 7340}; 7341 7342exports['default'] = extendFn; 7343 7344},{"86":86,"88":88}],44:[function(_dereq_,module,exports){ 7345'use strict'; 7346 7347exports.__esModule = true; 7348 7349var _document = _dereq_(94); 7350 7351var _document2 = _interopRequireDefault(_document); 7352 7353function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 7354 7355/** 7356 * Store the browser-specific methods for the fullscreen API. 7357 * 7358 * @type {Object} 7359 * @see [Specification]{@link https://fullscreen.spec.whatwg.org} 7360 * @see [Map Approach From Screenfull.js]{@link https://github.com/sindresorhus/screenfull.js} 7361 */ 7362var FullscreenApi = {}; 7363 7364// browser API methods 7365/** 7366 * @file fullscreen-api.js 7367 * @module fullscreen-api 7368 * @private 7369 */ 7370var apiMap = [['requestFullscreen', 'exitFullscreen', 'fullscreenElement', 'fullscreenEnabled', 'fullscreenchange', 'fullscreenerror'], 7371// WebKit 7372['webkitRequestFullscreen', 'webkitExitFullscreen', 'webkitFullscreenElement', 'webkitFullscreenEnabled', 'webkitfullscreenchange', 'webkitfullscreenerror'], 7373// Old WebKit (Safari 5.1) 7374['webkitRequestFullScreen', 'webkitCancelFullScreen', 'webkitCurrentFullScreenElement', 'webkitCancelFullScreen', 'webkitfullscreenchange', 'webkitfullscreenerror'], 7375// Mozilla 7376['mozRequestFullScreen', 'mozCancelFullScreen', 'mozFullScreenElement', 'mozFullScreenEnabled', 'mozfullscreenchange', 'mozfullscreenerror'], 7377// Microsoft 7378['msRequestFullscreen', 'msExitFullscreen', 'msFullscreenElement', 'msFullscreenEnabled', 'MSFullscreenChange', 'MSFullscreenError']]; 7379 7380var specApi = apiMap[0]; 7381var browserApi = void 0; 7382 7383// determine the supported set of functions 7384for (var i = 0; i < apiMap.length; i++) { 7385 // check for exitFullscreen function 7386 if (apiMap[i][1] in _document2['default']) { 7387 browserApi = apiMap[i]; 7388 break; 7389 } 7390} 7391 7392// map the browser API names to the spec API names 7393if (browserApi) { 7394 for (var _i = 0; _i < browserApi.length; _i++) { 7395 FullscreenApi[specApi[_i]] = browserApi[_i]; 7396 } 7397} 7398 7399exports['default'] = FullscreenApi; 7400 7401},{"94":94}],45:[function(_dereq_,module,exports){ 7402'use strict'; 7403 7404exports.__esModule = true; 7405 7406var _component = _dereq_(5); 7407 7408var _component2 = _interopRequireDefault(_component); 7409 7410function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 7411 7412function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 7413 7414function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 7415 7416function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
7416onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 7417 * @file loading-spinner.js 7418 */ 7419 7420 7421/** 7422 * A loading spinner for use during waiting/loading events. 7423 * 7424 * @extends Component 7425 */ 7426var LoadingSpinner = function (_Component) { 7427 _inherits(LoadingSpinner, _Component); 7428 7429 function LoadingSpinner() { 7430 _classCallCheck(this, LoadingSpinner); 7431 7432 return _possibleConstructorReturn(this, _Component.apply(this, arguments)); 7433 } 7434 7435 /** 7436 * Create the `LoadingSpinner`s DOM element. 7437 * 7438 * @return {Element} 7439 * The dom element that gets created. 7440 */ 7441 LoadingSpinner.prototype.createEl = function createEl() { 7442 return _Component.prototype.createEl.call(this, 'div', { 7443 className: 'vjs-loading-spinner', 7444 dir: 'ltr' 7445 }); 7446 }; 7447 7448 return LoadingSpinner; 7449}(_component2['default']); 7450 7451_component2['default'].registerComponent('LoadingSpinner', LoadingSpinner); 7452exports['default'] = LoadingSpinner; 7453 7454},{"5":5}],46:[function(_dereq_,module,exports){ 7455'use strict'; 7456 7457exports.__esModule = true; 7458 7459var _obj = _dereq_(88); 7460 7461/** 7462 * A Custom `MediaError` class which mimics the standard HTML5 `MediaError` class. 7463 * 7464 * @param {number|string|Object|MediaError} value 7465 * This can be of multiple types: 7466 * - number: should be a standard error code 7467 * - string: an error message (the code will be 0) 7468 * - Object: arbitrary properties 7469 * - `MediaError` (native): used to populate a video.js `MediaError` object 7470 * - `MediaError` (video.js): will return itself if it's already a 7471 * video.js `MediaError` object. 7472 * 7473 * @see [MediaError Spec]{@link https://dev.w3.org/html5/spec-author-view/video.html#mediaerror} 7474 * @see [Encrypted MediaError Spec]{@link https://www.w3.org/TR/2013/WD-encrypted-media-20130510/#error-codes} 7475 * 7476 * @class MediaError 7477 */ 7478function MediaError(value) { 7479 7480 // Allow redundant calls to this constructor to avoid having `instanceof` 7481 // checks peppered around the code. 7482 if (value instanceof MediaError) { 7483 return value; 7484 } 7485 7486 if (typeof value === 'number') { 7487 this.code = value; 7488 } else if (typeof value === 'string') {
7489 // default code is zero, so this is a custom error 7490 this.message = value; 7491 } else if ((0, _obj.isObject)(value)) { 7492 7493 // We assign the `code` property manually because native `MediaError` objects 7494 // do not expose it as an own/enumerable property of the object. 7495 if (typeof value.code === 'number') { 7496 this.code = value.code; 7497 } 7498 7499 (0, _obj.assign)(this, value); 7500 } 7501 7502 if (!this.message) { 7503 this.message = MediaError.defaultMessages[this.code] || ''; 7504 } 7505} 7506 7507/** 7508 * The error code that refers two one of the defined `MediaError` types 7509 * 7510 * @type {Number} 7511 */ 7512/** 7513 * @file media-error.js 7514 */ 7515MediaError.prototype.code = 0; 7516 7517/** 7518 * An optional message that to show with the error. Message is not part of the HTML5 7519 * video spec but allows for more informative custom errors. 7520 * 7521 * @type {String} 7522 */ 7523MediaError.prototype.message = ''; 7524 7525/** 7526 * An optional status code that can be set by plugins to allow even more detail about 7527 * the error. For example a plugin might provide a specific HTTP status code and an 7528 * error message for that code. Then when the plugin gets that error this class will 7529 * know how to display an error message for it. This allows a custom message to show 7530 * up on the `Player` error overlay. 7531 * 7532 * @type {Array} 7533 */ 7534MediaError.prototype.status = null; 7535 7536/** 7537 * Errors indexed by the W3C standard. The order **CANNOT CHANGE**! See the 7538 * specification listed under {@link MediaError} for more information. 7539 * 7540 * @enum {array} 7541 * @readonly 7542 * @property {string} 0 - MEDIA_ERR_CUSTOM 7543 * @property {string} 1 - MEDIA_ERR_CUSTOM 7544 * @property {string} 2 - MEDIA_ERR_ABORTED 7545 * @property {string} 3 - MEDIA_ERR_NETWORK 7546 * @property {string} 4 - MEDIA_ERR_SRC_NOT_SUPPORTED 7547 * @property {string} 5 - MEDIA_ERR_ENCRYPTED 7548 */ 7549MediaError.errorTypes = ['MEDIA_ERR_CUSTOM', 'MEDIA_ERR_ABORTED', 'MEDIA_ERR_NETWORK', 'MEDIA_ERR_DECODE', 'MEDIA_ERR_SRC_NOT_SUPPORTED', 'MEDIA_ERR_ENCRYPTED']; 7550 7551/** 7552 * The default `MediaError` messages based on the {@link MediaError.errorTypes}. 7553 * 7554 * @type {Array} 7555 * @constant 7556 */ 7557MediaError.defaultMessages = { 7558 1: 'You aborted the media playback', 7559 2: 'A network error caused the media download to fail part-way.', 7560 3: 'The media playback was aborted due to a corruption problem or because the media used features your browser did not support.', 7561 4: 'The media could not be loaded, either because the server or network failed or because the format is not supported.', 7562 5: 'The media is encrypted and we do not have the keys to decrypt it.' 7563}; 7564 7565// Add types as properties on MediaError 7566// e.g. MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED = 4; 7567for (var errNum = 0; errNum < MediaError.errorTypes.length; errNum++) { 7568 MediaError[MediaError.errorTypes[errNum]] = errNum; 7569 // values should be accessible on both the class and instance 7570 MediaError.prototype[MediaError.errorTypes[errNum]] = errNum; 7571} 7572 7573// jsdocs for instance/static members added above 7574// instance methods use `#` and static methods use `.` 7575/** 7576 * W3C error code for any custom error. 7577 * 7578 * @member MediaError#MEDIA_ERR_CUSTOM 7579 * @constant {number} 7580 * @default 0 7581 */ 7582/** 7583 * W3C error code for any custom error. 7584 * 7585 * @member MediaError.MEDIA_ERR_CUSTOM 7586 * @constant {number} 7587 * @default 0 7588 */ 7589 7590/** 7591 * W3C error code for media error aborted. 7592 * 7593 * @member MediaError#MEDIA_ERR_ABORTED 7594 * @constant {number} 7595 * @default 1 7596 */ 7597/** 7598 * W3C error code for media error aborted. 7599 * 7600 * @member MediaError.MEDIA_ERR_ABORTED 7601 * @constant {number} 7602 * @default 1 7603 */ 7604 7605/** 7606 * W3C error code for any network error. 7607 * 7608 * @member MediaError#MEDIA_ERR_NETWORK 7609 * @constant {number} 7610 * @default 2 7611 */ 7612/** 7613 * W3C error code for any network error. 7614 * 7615 * @member MediaError.MEDIA_ERR_NETWORK 7616 * @constant {number} 7617 * @default 2 7618 */ 7619 7620/** 7621 * W3C error code for any decoding error. 7622 * 7623 * @member MediaError#MEDIA_ERR_DECODE 7624 * @constant {number} 7625 * @default 3 7626 */ 7627/** 7628 * W3C error code for any decoding error. 7629 * 7630 * @member MediaError.MEDIA_ERR_DECODE 7631 * @constant {number} 7632 * @default 3 7633 */ 7634 7635/** 7636 * W3C error code for any time that a source is not supported. 7637 * 7638 * @member MediaError#MEDIA_ERR_SRC_NOT_SUPPORTED 7639 * @constant {number} 7640 * @default 4 7641 */ 7642/** 7643 * W3C error code for any time that a source is not supported. 7644 * 7645 * @member MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED 7646 * @constant {number} 7647 * @default 4 7648 */ 7649 7650/** 7651 * W3C error code for any time that a source is encrypted. 7652 * 7653 * @member MediaError#MEDIA_ERR_ENCRYPTED 7654 * @constant {number} 7655 * @default 5 7656 */ 7657/** 7658 * W3C error code for any time that a source is encrypted. 7659 * 7660 * @member MediaError.MEDIA_ERR_ENCRYPTED 7661 * @constant {number} 7662 * @default 5 7663 */ 7664 7665exports['default'] = MediaError; 7666 7667},{"88":88}],47:[function(_dereq_,module,exports){ 7668'use strict'; 7669 7670exports.__esModule = true; 7671 7672var _clickableComponent = _dereq_(3); 7673 7674var _clickableComponent2 = _interopRequireDefault(_clickableComponent); 7675 7676var _component = _dereq_(5); 7677 7678var _component2 = _interopRequireDefault(_component); 7679 7680var _menu = _dereq_(49); 7681 7682var _menu2 = _interopRequireDefault(_menu); 7683 7684var _dom = _dereq_(81); 7685 7686var Dom = _interopRequireWildcard(_dom); 7687 7688var _fn = _dereq_(83); 7689 7690var Fn = _interopRequireWildcard(_fn); 7691 7692var _toTitleCase = _dereq_(91); 7693 7694var _toTitleCase2 = _interopRequireDefault(_toTitleCase); 7695 7696function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 7697 7698function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 7699 7700function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 7701 7702function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 7703 7704function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
vendor: 5,173 bytes, lines 7704-7869
7704onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 7705 * @file menu-button.js 7706 */ 7707 7708 7709/** 7710 * A `MenuButton` class for any popup {@link Menu}. 7711 * 7712 * @extends ClickableComponent 7713 */ 7714var MenuButton = function (_ClickableComponent) { 7715 _inherits(MenuButton, _ClickableComponent); 7716 7717 /** 7718 * Creates an instance of this class. 7719 * 7720 * @param {Player} player 7721 * The `Player` that this class should be attached to. 7722 * 7723 * @param {Object} [options={}] 7724 * The key/value store of player options. 7725 */ 7726 function MenuButton(player) { 7727 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 7728 7729 _classCallCheck(this, MenuButton); 7730 7731 var _this = _possibleConstructorReturn(this, _ClickableComponent.call(this, player, options)); 7732 7733 _this.update(); 7734 7735 _this.enabled_ = true; 7736 7737 _this.el_.setAttribute('aria-haspopup', 'true'); 7738 _this.el_.setAttribute('role', 'menuitem'); 7739 _this.on('keydown', _this.handleSubmenuKeyPress); 7740 return _this; 7741 } 7742 7743 /** 7744 * Update the menu based on the current state of its items. 7745 */ 7746 7747 7748 MenuButton.prototype.update = function update() { 7749 var menu = this.createMenu(); 7750 7751 if (this.menu) { 7752 this.removeChild(this.menu); 7753 } 7754 7755 this.menu = menu; 7756 this.addChild(menu); 7757 7758 /** 7759 * Track the state of the menu button 7760 * 7761 * @type {Boolean} 7762 * @private 7763 */ 7764 this.buttonPressed_ = false; 7765 this.el_.setAttribute('aria-expanded', 'false'); 7766 7767 if (this.items && this.items.length <= this.hideThreshold_) { 7768 this.hide(); 7769 } else { 7770 this.show(); 7771 } 7772 }; 7773 7774 /** 7775 * Create the menu and add all items to it. 7776 * 7777 * @return {Menu} 7778 * The constructed menu 7779 */ 7780 7781 7782 MenuButton.prototype.createMenu = function createMenu() { 7783 var menu = new _menu2['default'](this.player_); 7784 7785 /** 7786 * Hide the menu if the number of items is less than or equal to this threshold. This defaults 7787 * to 0 and whenever we add items which can be hidden to the menu we'll increment it. We list 7788 * it here because every time we run `createMenu` we need to reset the value. 7789 * 7790 * @protected 7791 * @type {Number} 7792 */ 7793 this.hideThreshold_ = 0; 7794 7795 // Add a title list item to the top 7796 if (this.options_.title) { 7797 var title = Dom.createEl('li', { 7798 className: 'vjs-menu-title', 7799 innerHTML: (0, _toTitleCase2['default'])(this.options_.title), 7800 tabIndex: -1 7801 }); 7802 7803 this.hideThreshold_ += 1; 7804 7805 menu.children_.unshift(title); 7806 Dom.insertElFirst(title, menu.contentEl()); 7807 } 7808 7809 this.items = this.createItems(); 7810 7811 if (this.items) { 7812 // Add menu items to the menu 7813 for (var i = 0; i < this.items.length; i++) { 7814 menu.addItem(this.items[i]); 7815 } 7816 } 7817 7818 return menu; 7819 }; 7820 7821 /** 7822 * Create the list of menu items. Specific to each subclass. 7823 * 7824 * @abstract 7825 */ 7826 7827 7828 MenuButton.prototype.createItems = function createItems() {}; 7829 7830 /** 7831 * Create the `MenuButtons`s DOM element. 7832 * 7833 * @return {Element} 7834 * The element that gets created. 7835 */ 7836 7837 7838 MenuButton.prototype.createEl = function createEl() { 7839 return _ClickableComponent.prototype.createEl.call(this, 'div', { 7840 className: this.buildCSSClass() 7841 }); 7842 }; 7843 7844 /** 7845 * Builds the default DOM `className`. 7846 * 7847 * @return {string} 7848 * The DOM `className` for this object. 7849 */ 7850 7851 7852 MenuButton.prototype.buildCSSClass = function buildCSSClass() { 7853 var menuButtonClass = 'vjs-menu-button'; 7854 7855 // If the inline option is passed, we want to use different styles altogether. 7856 if (this.options_.inline === true) { 7857 menuButtonClass += '-inline'; 7858 } else { 7859 menuButtonClass += '-popup'; 7860 } 7861 7862 return 'vjs-menu-button ' + menuButtonClass + ' ' + _ClickableComponent.prototype.buildCSSClass.call(this); 7863 }; 7864 7865 /** 7866 * Handle a click on a `MenuButton`. 7867 * See {@link ClickableComponent#handleClick} for instances where this is called. 7868 * 7869 * @param {EventTarget~Event} event
7870 * The `keydown`, `tap`, or `click` event that caused this function to be 7871 * called. 7872 * 7873 * @listens tap 7874 * @listens click 7875 */ 7876 7877 7878 MenuButton.prototype.handleClick = function handleClick(event) { 7879 // When you click the button it adds focus, which will show the menu. 7880 // So we'll remove focus when the mouse leaves the button. Focus is needed 7881 // for tab navigation. 7882 7883 this.one(this.menu.contentEl(), 'mouseleave', Fn.bind(this, function (e) { 7884 this.unpressButton(); 7885 this.el_.blur(); 7886 })); 7887 if (this.buttonPressed_) { 7888 this.unpressButton(); 7889 } else { 7890 this.pressButton(); 7891 } 7892 }; 7893 7894 /** 7895 * Handle tab, escape, down arrow, and up arrow keys for `MenuButton`. See 7896 * {@link ClickableComponent#handleKeyPress} for instances where this is called. 7897 * 7898 * @param {EventTarget~Event} event 7899 * The `keydown` event that caused this function to be called. 7900 * 7901 * @listens keydown 7902 */ 7903 7904 7905 MenuButton.prototype.handleKeyPress = function handleKeyPress(event) { 7906 7907 // Escape (27) key or Tab (9) key unpress the 'button' 7908 if (event.which === 27 || event.which === 9) { 7909 if (this.buttonPressed_) { 7910 this.unpressButton(); 7911 } 7912 // Don't preventDefault for Tab key - we still want to lose focus 7913 if (event.which !== 9) { 7914 event.preventDefault(); 7915 } 7916 // Up (38) key or Down (40) key press the 'button' 7917 } else if (event.which === 38 || event.which === 40) { 7918 if (!this.buttonPressed_) { 7919 this.pressButton(); 7920 event.preventDefault(); 7921 } 7922 } else { 7923 _ClickableComponent.prototype.handleKeyPress.call(this, event); 7924 } 7925 }; 7926 7927 /** 7928 * Handle a `keydown` event on a sub-menu. The listener for this is added in 7929 * the constructor. 7930 * 7931 * @param {EventTarget~Event} event 7932 * Key press event 7933 * 7934 * @listens keydown 7935 */ 7936 7937 7938 MenuButton.prototype.handleSubmenuKeyPress = function handleSubmenuKeyPress(event) { 7939 7940 // Escape (27) key or Tab (9) key unpress the 'button' 7941 if (event.which === 27 || event.which === 9) { 7942 if (this.buttonPressed_) { 7943 this.unpressButton(); 7944 } 7945 // Don't preventDefault for Tab key - we still want to lose focus 7946 if (event.which !== 9) { 7947 event.preventDefault(); 7948 } 7949 } 7950 }; 7951 7952 /** 7953 * Put the current `MenuButton` into a pressed state. 7954 */ 7955 7956 7957 MenuButton.prototype.pressButton = function pressButton() { 7958 if (this.enabled_) { 7959 this.buttonPressed_ = true; 7960 this.menu.lockShowing(); 7961 this.el_.setAttribute('aria-expanded', 'true'); 7962 // set the focus into the submenu 7963 this.menu.focus(); 7964 } 7965 }; 7966 7967 /** 7968 * Take the current `MenuButton` out of a pressed state. 7969 */ 7970 7971 7972 MenuButton.prototype.unpressButton = function unpressButton() { 7973 if (this.enabled_) { 7974 this.buttonPressed_ = false; 7975 this.menu.unlockShowing(); 7976 this.el_.setAttribute('aria-expanded', 'false'); 7977 // Set focus back to this menu button 7978 this.el_.focus(); 7979 } 7980 }; 7981 7982 /** 7983 * Disable the `MenuButton`. Don't allow it to be clicked. 7984 * 7985 * @return {MenuButton} 7986 * Returns itself; method can be chained. 7987 */ 7988 7989 7990 MenuButton.prototype.disable = function disable() { 7991 // Unpress, but don't force focus on this button 7992 this.buttonPressed_ = false; 7993 this.menu.unlockShowing(); 7994 this.el_.setAttribute('aria-expanded', 'false'); 7995 7996 this.enabled_ = false; 7997 7998 return _ClickableComponent.prototype.disable.call(this); 7999 }; 8000 8001 /** 8002 * Enable the `MenuButton`. Allow it to be clicked. 8003 * 8004 * @return {MenuButton} 8005 * Returns itself; method can be chained. 8006 */ 8007 8008 8009 MenuButton.prototype.enable = function enable() { 8010 this.enabled_ = true; 8011 8012 return _ClickableComponent.prototype.enable.call(this); 8013 }; 8014 8015 return MenuButton; 8016}(_clickableComponent2['default']); 8017 8018_component2['default'].registerComponent('MenuButton', MenuButton); 8019exports['default'] = MenuButton; 8020 8021},{"3":3,"49":49,"5":5,"81":81,"83":83,"91":91}],48:[function(_dereq_,module,exports){ 8022'use strict'; 8023 8024exports.__esModule = true; 8025 8026var _clickableComponent = _dereq_(3); 8027 8028var _clickableComponent2 = _interopRequireDefault(_clickableComponent); 8029 8030var _component = _dereq_(5); 8031 8032var _component2 = _interopRequireDefault(_component); 8033 8034var _obj = _dereq_(88); 8035 8036function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 8037 8038function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 8039 8040function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 8041 8042function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
8042onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 8043 * @file menu-item.js 8044 */ 8045 8046 8047/** 8048 * The component for a menu item. `<li>` 8049 * 8050 * @extends ClickableComponent 8051 */ 8052var MenuItem = function (_ClickableComponent) { 8053 _inherits(MenuItem, _ClickableComponent); 8054 8055 /** 8056 * Creates an instance of the this class. 8057 * 8058 * @param {Player} player 8059 * The `Player` that this class should be attached to. 8060 * 8061 * @param {Object} [options={}] 8062 * The key/value store of player options. 8063 * 8064 */ 8065 function MenuItem(player, options) { 8066 _classCallCheck(this, MenuItem); 8067 8068 var _this = _possibleConstructorReturn(this, _ClickableComponent.call(this, player, options)); 8069 8070 _this.selectable = options.selectable; 8071 8072 _this.selected(options.selected); 8073 8074 if (_this.selectable) { 8075 // TODO: May need to be either menuitemcheckbox or menuitemradio, 8076 // and may need logical grouping of menu items. 8077 _this.el_.setAttribute('role', 'menuitemcheckbox'); 8078 } else { 8079 _this.el_.setAttribute('role', 'menuitem'); 8080 } 8081 return _this; 8082 } 8083 8084 /** 8085 * Create the `MenuItem's DOM element 8086 * 8087 * @param {string} [type=li] 8088 * Element's node type, not actually used, always set to `li`. 8089 * 8090 * @param {Object} [props={}] 8091 * An object of properties that should be set on the element 8092 * 8093 * @param {Object} [attrs={}] 8094 * An object of attributes that should be set on the element 8095 * 8096 * @return {Element} 8097 * The element that gets created. 8098 */ 8099 8100 8101 MenuItem.prototype.createEl = function createEl(type, props, attrs) { 8102 // The control is textual, not just an icon 8103 this.nonIconControl = true; 8104 8105 return _ClickableComponent.prototype.createEl.call(this, 'li', (0, _obj.assign)({ 8106 className: 'vjs-menu-item', 8107 innerHTML: this.localize(this.options_.label), 8108 tabIndex: -1 8109 }, props), attrs); 8110 }; 8111 8112 /** 8113 * Any click on a `MenuItem` puts int into the selected state. 8114 * See {@link ClickableComponent#handleClick} for instances where this is called. 8115 * 8116 * @param {EventTarget~Event} event 8117 * The `keydown`, `tap`, or `click` event that caused this function to be 8118 * called. 8119 * 8120 * @listens tap 8121 * @listens click 8122 */ 8123 8124 8125 MenuItem.prototype.handleClick = function handleClick(event) { 8126 this.selected(true); 8127 }; 8128 8129 /** 8130 * Set the state for this menu item as selected or not. 8131 * 8132 * @param {boolean} selected 8133 * if the menu item is selected or not 8134 */ 8135 8136 8137 MenuItem.prototype.selected = function selected(_selected) { 8138 if (this.selectable) { 8139 if (_selected) { 8140 this.addClass('vjs-selected'); 8141 this.el_.setAttribute('aria-checked', 'true'); 8142 // aria-checked isn't fully supported by browsers/screen readers, 8143 // so indicate selected state to screen reader in the control text. 8144 this.controlText(', selected'); 8145 } else { 8146 this.removeClass('vjs-selected'); 8147 this.el_.setAttribute('aria-checked', 'false'); 8148 // Indicate un-selected state to screen reader 8149 // Note that a space clears out the selected state text 8150 this.controlText(' '); 8151 } 8152 } 8153 }; 8154
8155 return MenuItem; 8156}(_clickableComponent2['default']); 8157 8158_component2['default'].registerComponent('MenuItem', MenuItem); 8159exports['default'] = MenuItem; 8160 8161},{"3":3,"5":5,"88":88}],49:[function(_dereq_,module,exports){ 8162'use strict'; 8163 8164exports.__esModule = true; 8165 8166var _component = _dereq_(5); 8167 8168var _component2 = _interopRequireDefault(_component); 8169 8170var _dom = _dereq_(81); 8171 8172var Dom = _interopRequireWildcard(_dom); 8173 8174var _fn = _dereq_(83); 8175 8176var Fn = _interopRequireWildcard(_fn); 8177 8178var _events = _dereq_(82); 8179 8180var Events = _interopRequireWildcard(_events); 8181 8182function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 8183 8184function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 8185 8186function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 8187 8188function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 8189 8190function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 8191 * @file menu.js 8192 */ 8193 8194 8195/** 8196 * The Menu component is used to build popup menus, including subtitle and 8197 * captions selection menus. 8198 * 8199 * @extends Component 8200 */ 8201var Menu = function (_Component) { 8202 _inherits(Menu, _Component); 8203 8204 /** 8205 * Create an instance of this class. 8206 * 8207 * @param {Player} player 8208 * the player that this component should attach to 8209 * 8210 * @param {Object} [options] 8211 * Object of option names and values 8212 * 8213 */ 8214 function Menu(player, options) { 8215 _classCallCheck(this, Menu); 8216 8217 var _this = _possibleConstructorReturn(this, _Component.call(this, player, options)); 8218 8219 _this.focusedChild_ = -1; 8220 8221 _this.on('keydown', _this.handleKeyPress); 8222 return _this; 8223 } 8224 8225 /** 8226 * Add a {@link MenuItem} to the menu. 8227 * 8228 * @param {Object|string} component 8229 * The name or instance of the `MenuItem` to add. 8230 * 8231 */ 8232 8233 8234 Menu.prototype.addItem = function addItem(component) { 8235 this.addChild(component); 8236 component.on('click', Fn.bind(this, function (event) { 8237 this.unlockShowing(); 8238 // TODO: Need to set keyboard focus back to the menuButton 8239 })); 8240 }; 8241 8242 /** 8243 * Create the `Menu`s DOM element. 8244 * 8245 * @return {Element} 8246 * the element that was created 8247 */ 8248 8249 8250 Menu.prototype.createEl = function createEl() { 8251 var contentElType = this.options_.contentElType || 'ul'; 8252 8253 this.contentEl_ = Dom.createEl(contentElType, { 8254 className: 'vjs-menu-content' 8255 }); 8256 8257 this.contentEl_.setAttribute('role', 'menu'); 8258 8259 var el = _Component.prototype.createEl.call(this, 'div', { 8260 append: this.contentEl_, 8261 className: 'vjs-menu' 8262 }); 8263 8264 el.setAttribute('role', 'presentation'); 8265 el.appendChild(this.contentEl_); 8266 8267 // Prevent clicks from bubbling up. Needed for Menu Buttons, 8268 // where a click on the parent is significant 8269 Events.on(el, 'click', function (event) { 8270 event.preventDefault(); 8271 event.stopImmediatePropagation(); 8272 }); 8273 8274 return el; 8275 }; 8276 8277 /** 8278 * Handle a `keydown` event on this menu. This listener is added in the constructor. 8279 * 8280 * @param {EventTarget~Event} event 8281 * A `keydown` event that happened on the menu. 8282 * 8283 * @listens keydown 8284 */ 8285 8286 8287 Menu.prototype.handleKeyPress = function handleKeyPress(event) { 8288 // Left and Down Arrows 8289 if (event.which === 37 || event.which === 40) { 8290 event.preventDefault(); 8291 this.stepForward(); 8292 8293 // Up and Right Arrows 8294 } else if (event.which === 38 || event.which === 39) { 8295 event.preventDefault(); 8296 this.stepBack(); 8297 } 8298 }; 8299 8300 /** 8301 * Move to next (lower) menu item for keyboard users. 8302 */ 8303 8304 8305 Menu.prototype.stepForward = function stepForward() {
8306 var stepChild = 0; 8307 8308 if (this.focusedChild_ !== undefined) { 8309 stepChild = this.focusedChild_ + 1; 8310 } 8311 this.focus(stepChild); 8312 }; 8313 8314 /** 8315 * Move to previous (higher) menu item for keyboard users. 8316 */ 8317 8318 8319 Menu.prototype.stepBack = function stepBack() { 8320 var stepChild = 0; 8321 8322 if (this.focusedChild_ !== undefined) { 8323 stepChild = this.focusedChild_ - 1; 8324 } 8325 this.focus(stepChild); 8326 }; 8327 8328 /** 8329 * Set focus on a {@link MenuItem} in the `Menu`. 8330 * 8331 * @param {Object|string} [item=0] 8332 * Index of child item set focus on. 8333 */ 8334 8335 8336 Menu.prototype.focus = function focus() { 8337 var item = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; 8338 8339 var children = this.children().slice(); 8340 var haveTitle = children.length && children[0].className && /vjs-menu-title/.test(children[0].className); 8341 8342 if (haveTitle) { 8343 children.shift(); 8344 } 8345 8346 if (children.length > 0) { 8347 if (item < 0) { 8348 item = 0; 8349 } else if (item >= children.length) { 8350 item = children.length - 1; 8351 } 8352 8353 this.focusedChild_ = item; 8354 8355 children[item].el_.focus(); 8356 } 8357 }; 8358 8359 return Menu; 8360}(_component2['default']); 8361 8362_component2['default'].registerComponent('Menu', Menu); 8363exports['default'] = Menu; 8364 8365},{"5":5,"81":81,"82":82,"83":83}],50:[function(_dereq_,module,exports){ 8366'use strict'; 8367 8368exports.__esModule = true; 8369 8370var _dom = _dereq_(81); 8371 8372var Dom = _interopRequireWildcard(_dom); 8373 8374var _fn = _dereq_(83); 8375 8376var Fn = _interopRequireWildcard(_fn); 8377 8378var _component = _dereq_(5); 8379 8380var _component2 = _interopRequireDefault(_component); 8381 8382function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 8383 8384function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 8385 8386function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 8387 8388function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 8389 8390function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 8391 * @file modal-dialog.js 8392 */ 8393 8394 8395var MODAL_CLASS_NAME = 'vjs-modal-dialog'; 8396var ESC = 27; 8397 8398/** 8399 * The `ModalDialog` displays over the video and its controls, which blocks 8400 * interaction with the player until it is closed. 8401 * 8402 * Modal dialogs include a "Close" button and will close when that button 8403 * is activated - or when ESC is pressed anywhere. 8404 * 8405 * @extends Component 8406 */ 8407 8408var ModalDialog = function (_Component) { 8409 _inherits(ModalDialog, _Component); 8410 8411 /** 8412 * Create an instance of this class. 8413 * 8414 * @param {Player} player 8415 * The `Player` that this class should be attached to. 8416 * 8417 * @param {Object} [options] 8418 * The key/value store of player options. 8419 * 8420 * @param {Mixed} [options.content=undefined] 8421 * Provide customized content for this modal. 8422 * 8423 * @param {string} [options.description] 8424 * A text description for the modal, primarily for accessibility. 8425 * 8426 * @param {boolean} [options.fillAlways=false] 8427 * Normally, modals are automatically filled only the first time 8428 * they open. This tells the modal to refresh its content 8429 * every time it opens. 8430 * 8431 * @param {string} [options.label] 8432 * A text label for the modal, primarily for accessibility. 8433 * 8434 * @param {boolean} [options.temporary=true] 8435 * If `true`, the modal can only be opened once; it will be 8436 * disposed as soon as it's closed. 8437 * 8438 * @param {boolean} [options.uncloseable=false] 8439 * If `true`, the user will not be able to close the modal 8440 * through the UI in the normal ways. Programmatic closing is 8441 * still possible. 8442 */ 8443 function ModalDialog(player, options) { 8444 _classCallCheck(this, ModalDialog); 8445 8446 var _this = _possibleConstructorReturn(this, _Component.call(this, player, options)); 8447 8448 _this.opened_ = _this.hasBeenOpened_ = _this.hasBeenFilled_ = false;
vendor: 5,078 bytes, lines 8449-8646
8449 8450 _this.closeable(!_this.options_.uncloseable); 8451 _this.content(_this.options_.content); 8452 8453 // Make sure the contentEl is defined AFTER any children are initialized 8454 // because we only want the contents of the modal in the contentEl 8455 // (not the UI elements like the close button). 8456 _this.contentEl_ = Dom.createEl('div', { 8457 className: MODAL_CLASS_NAME + '-content' 8458 }, { 8459 role: 'document' 8460 }); 8461 8462 _this.descEl_ = Dom.createEl('p', { 8463 className: MODAL_CLASS_NAME + '-description vjs-offscreen', 8464 id: _this.el().getAttribute('aria-describedby') 8465 }); 8466 8467 Dom.textContent(_this.descEl_, _this.description()); 8468 _this.el_.appendChild(_this.descEl_); 8469 _this.el_.appendChild(_this.contentEl_); 8470 return _this; 8471 } 8472 8473 /** 8474 * Create the `ModalDialog`'s DOM element 8475 * 8476 * @return {Element} 8477 * The DOM element that gets created. 8478 */ 8479 8480 8481 ModalDialog.prototype.createEl = function createEl() { 8482 return _Component.prototype.createEl.call(this, 'div', { 8483 className: this.buildCSSClass(), 8484 tabIndex: -1 8485 }, { 8486 'aria-describedby': this.id() + '_description', 8487 'aria-hidden': 'true', 8488 'aria-label': this.label(), 8489 'role': 'dialog' 8490 }); 8491 }; 8492 8493 /** 8494 * Builds the default DOM `className`. 8495 * 8496 * @return {string} 8497 * The DOM `className` for this object. 8498 */ 8499 8500 8501 ModalDialog.prototype.buildCSSClass = function buildCSSClass() { 8502 return MODAL_CLASS_NAME + ' vjs-hidden ' + _Component.prototype.buildCSSClass.call(this); 8503 }; 8504 8505 /** 8506 * Handles `keydown` events on the document, looking for ESC, which closes 8507 * the modal. 8508 * 8509 * @param {EventTarget~Event} e 8510 * The keypress that triggered this event. 8511 * 8512 * @listens keydown 8513 */ 8514 8515 8516 ModalDialog.prototype.handleKeyPress = function handleKeyPress(e) { 8517 if (e.which === ESC && this.closeable()) { 8518 this.close(); 8519 } 8520 }; 8521 8522 /** 8523 * Returns the label string for this modal. Primarily used for accessibility. 8524 * 8525 * @return {string} 8526 * the localized or raw label of this modal. 8527 */ 8528 8529 8530 ModalDialog.prototype.label = function label() { 8531 return this.options_.label || this.localize('Modal Window'); 8532 }; 8533 8534 /** 8535 * Returns the description string for this modal. Primarily used for 8536 * accessibility. 8537 * 8538 * @return {string} 8539 * The localized or raw description of this modal. 8540 */ 8541 8542 8543 ModalDialog.prototype.description = function description() { 8544 var desc = this.options_.description || this.localize('This is a modal window.'); 8545 8546 // Append a universal closeability message if the modal is closeable. 8547 if (this.closeable()) { 8548 desc += ' ' + this.localize('This modal can be closed by pressing the Escape key or activating the close button.'); 8549 } 8550 8551 return desc; 8552 }; 8553 8554 /** 8555 * Opens the modal. 8556 * 8557 * @fires ModalDialog#beforemodalopen 8558 * @fires ModalDialog#modalopen 8559 * 8560 * @return {ModalDialog} 8561 * Returns itself; method can be chained. 8562 */ 8563 8564 8565 ModalDialog.prototype.open = function open() { 8566 if (!this.opened_) { 8567 var player = this.player(); 8568 8569 /** 8570 * Fired just before a `ModalDialog` is opened. 8571 * 8572 * @event ModalDialog#beforemodalopen 8573 * @type {EventTarget~Event} 8574 */ 8575 this.trigger('beforemodalopen'); 8576 this.opened_ = true; 8577 8578 // Fill content if the modal has never opened before and 8579 // never been filled. 8580 if (this.options_.fillAlways || !this.hasBeenOpened_ && !this.hasBeenFilled_) { 8581 this.fill(); 8582 } 8583 8584 // If the player was playing, pause it and take note of its previously 8585 // playing state. 8586 this.wasPlaying_ = !player.paused(); 8587 8588 if (this.options_.pauseOnOpen && this.wasPlaying_) { 8589 player.pause(); 8590 } 8591 8592 if (this.closeable()) { 8593 this.on(this.el_.ownerDocument, 'keydown', Fn.bind(this, this.handleKeyPress)); 8594 } 8595 8596 player.controls(false); 8597 this.show(); 8598 this.el().setAttribute('aria-hidden', 'false'); 8599 8600 /** 8601 * Fired just after a `ModalDialog` is opened. 8602 * 8603 * @event ModalDialog#modalopen 8604 * @type {EventTarget~Event} 8605 */ 8606 this.trigger('modalopen'); 8607 this.hasBeenOpened_ = true; 8608 } 8609 return this; 8610 }; 8611 8612 /** 8613 * If the `ModalDialog` is currently open or closed. 8614 * 8615 * @param {boolean} [value] 8616 * If given, it will open (`true`) or close (`false`) the modal. 8617 * 8618 * @return {boolean} 8619 * the current open state of the modaldialog 8620 */ 8621 8622 8623 ModalDialog.prototype.opened = function opened(value) { 8624 if (typeof value === 'boolean') { 8625 this[value ? 'open' : 'close'](); 8626 } 8627 return this.opened_; 8628 }; 8629 8630 /** 8631 * Closes the modal, does nothing if the `ModalDialog` is 8632 * not open. 8633 * 8634 * @fires ModalDialog#beforemodalclose 8635 * @fires ModalDialog#modalclose 8636 * 8637 * @return {ModalDialog} 8638 * Returns itself; method can be chained. 8639 */ 8640 8641 8642 ModalDialog.prototype.close = function close() { 8643 if (this.opened_) { 8644 var player = this.player(); 8645 8646 /**
8647 * Fired just before a `ModalDialog` is closed. 8648 * 8649 * @event ModalDialog#beforemodalclose 8650 * @type {EventTarget~Event} 8651 */ 8652 this.trigger('beforemodalclose'); 8653 this.opened_ = false; 8654 8655 if (this.wasPlaying_ && this.options_.pauseOnOpen) { 8656 player.play(); 8657 } 8658 8659 if (this.closeable()) { 8660 this.off(this.el_.ownerDocument, 'keydown', Fn.bind(this, this.handleKeyPress)); 8661 } 8662 8663 player.controls(true); 8664 this.hide(); 8665 this.el().setAttribute('aria-hidden', 'true'); 8666 8667 /** 8668 * Fired just after a `ModalDialog` is closed. 8669 * 8670 * @event ModalDialog#modalclose 8671 * @type {EventTarget~Event} 8672 */ 8673 this.trigger('modalclose'); 8674 8675 if (this.options_.temporary) { 8676 this.dispose(); 8677 } 8678 } 8679 return this; 8680 }; 8681 8682 /** 8683 * Check to see if the `ModalDialog` is closeable via the UI. 8684 * 8685 * @param {boolean} [value] 8686 * If given as a boolean, it will set the `closeable` option. 8687 * 8688 * @return {boolean} 8689 * Returns the final value of the closable option. 8690 */ 8691 8692 8693 ModalDialog.prototype.closeable = function closeable(value) { 8694 if (typeof value === 'boolean') { 8695 var closeable = this.closeable_ = !!value; 8696 var close = this.getChild('closeButton'); 8697 8698 // If this is being made closeable and has no close button, add one. 8699 if (closeable && !close) { 8700 8701 // The close button should be a child of the modal - not its 8702 // content element, so temporarily change the content element. 8703 var temp = this.contentEl_; 8704 8705 this.contentEl_ = this.el_; 8706 close = this.addChild('closeButton', { controlText: 'Close Modal Dialog' }); 8707 this.contentEl_ = temp; 8708 this.on(close, 'close', this.close); 8709 } 8710 8711 // If this is being made uncloseable and has a close button, remove it. 8712 if (!closeable && close) { 8713 this.off(close, 'close', this.close); 8714 this.removeChild(close); 8715 close.dispose(); 8716 } 8717 } 8718 return this.closeable_; 8719 }; 8720 8721 /**
8722 * Fill the modal's content element with the modal's "content" option. 8723 * The content element will be emptied before this change takes place. 8724 * 8725 * @return {ModalDialog} 8726 * Returns itself; method can be chained. 8727 */ 8728 8729 8730 ModalDialog.prototype.fill = function fill() { 8731 return this.fillWith(this.content()); 8732 }; 8733 8734 /** 8735 * Fill the modal's content element with arbitrary content. 8736 * The content element will be emptied before this change takes place. 8737 * 8738 * @fires ModalDialog#beforemodalfill 8739 * @fires ModalDialog#modalfill 8740 * 8741 * @param {Mixed} [content] 8742 * The same rules apply to this as apply to the `content` option. 8743 * 8744 * @return {ModalDialog} 8745 * Returns itself; method can be chained. 8746 */ 8747 8748 8749 ModalDialog.prototype.fillWith = function fillWith(content) { 8750 var contentEl = this.contentEl(); 8751 var parentEl = contentEl.parentNode; 8752 var nextSiblingEl = contentEl.nextSibling; 8753 8754 /** 8755 * Fired just before a `ModalDialog` is filled with content. 8756 * 8757 * @event ModalDialog#beforemodalfill 8758 * @type {EventTarget~Event} 8759 */ 8760 this.trigger('beforemodalfill'); 8761 this.hasBeenFilled_ = true; 8762 8763 // Detach the content element from the DOM before performing 8764 // manipulation to avoid modifying the live DOM multiple times. 8765 parentEl.removeChild(contentEl); 8766 this.empty(); 8767 Dom.insertContent(contentEl, content); 8768 /** 8769 * Fired just after a `ModalDialog` is filled with content. 8770 * 8771 * @event ModalDialog#modalfill 8772 * @type {EventTarget~Event} 8773 */ 8774 this.trigger('modalfill'); 8775 8776 // Re-inject the re-filled content element. 8777 if (nextSiblingEl) { 8778 parentEl.insertBefore(contentEl, nextSiblingEl); 8779 } else { 8780 parentEl.appendChild(contentEl); 8781 } 8782 8783 return this; 8784 }; 8785 8786 /** 8787 * Empties the content element. This happens anytime the modal is filled. 8788 * 8789 * @fires ModalDialog#beforemodalempty 8790 * @fires ModalDialog#modalempty 8791 * 8792 * @return {ModalDialog} 8793 * Returns itself; method can be chained. 8794 */ 8795 8796 8797 ModalDialog.prototype.empty = function empty() { 8798 /** 8799 * Fired just before a `ModalDialog` is emptied. 8800 * 8801 * @event ModalDialog#beforemodalempty 8802 * @type {EventTarget~Event} 8803 */ 8804 this.trigger('beforemodalempty'); 8805 Dom.emptyEl(this.contentEl()); 8806 8807 /** 8808 * Fired just after a `ModalDialog` is emptied. 8809 * 8810 * @event ModalDialog#modalempty 8811 * @type {EventTarget~Event} 8812 */ 8813 this.trigger('modalempty'); 8814 return this; 8815 }; 8816 8817 /** 8818 * Gets or sets the modal content, which gets normalized before being 8819 * rendered into the DOM. 8820 * 8821 * This does not update the DOM or fill the modal, but it is called during 8822 * that process. 8823 * 8824 * @param {Mixed} [value] 8825 * If defined, sets the internal content value to be used on the 8826 * next call(s) to `fill`. This value is normalized before being 8827 * inserted. To "clear" the internal content value, pass `null`. 8828 * 8829 * @return {Mixed} 8830 * The current content of the modal dialog 8831 */ 8832 8833 8834 ModalDialog.prototype.content = function content(value) { 8835 if (typeof value !== 'undefined') { 8836 this.content_ = value; 8837 } 8838 return this.content_; 8839 }; 8840 8841 return ModalDialog; 8842}(_component2['default']); 8843 8844/** 8845 * Default options for `ModalDialog` default options. 8846 * 8847 * @type {Object} 8848 * @private 8849 */ 8850 8851 8852ModalDialog.prototype.options_ = { 8853 pauseOnOpen: true, 8854 temporary: true 8855}; 8856 8857_component2['default'].registerComponent('ModalDialog', ModalDialog); 8858exports['default'] = ModalDialog; 8859 8860},{"5":5,"81":81,"83":83}],51:[function(_dereq_,module,exports){ 8861'use strict'; 8862 8863exports.__esModule = true; 8864 8865var _component = _dereq_(5); 8866 8867var _component2 = _interopRequireDefault(_component); 8868 8869var _document = _dereq_(94); 8870 8871var _document2 = _interopRequireDefault(_document); 8872 8873var _window = _dereq_(95); 8874 8875var _window2 = _interopRequireDefault(_window); 8876 8877var _events = _dereq_(82); 8878 8879var Events = _interopRequireWildcard(_events); 8880 8881var _dom = _dereq_(81); 8882 8883var Dom = _interopRequireWildcard(_dom); 8884 8885var _fn = _dereq_(83); 8886 8887var Fn = _interopRequireWildcard(_fn); 8888 8889var _guid = _dereq_(85); 8890 8891var Guid = _interopRequireWildcard(_guid); 8892 8893var _browser = _dereq_(78); 8894 8895var browser = _interopRequireWildcard(_browser); 8896 8897var _log = _dereq_(86); 8898 8899var _log2 = _interopRequireDefault(_log); 8900 8901var _toTitleCase = _dereq_(91); 8902 8903var _toTitleCase2 = _interopRequireDefault(_toTitleCase); 8904 8905var _timeRanges = _dereq_(90); 8906 8907var _buffer = _dereq_(79); 8908 8909var _stylesheet = _dereq_(89); 8910 8911var stylesheet = _interopRequireWildcard(_stylesheet); 8912 8913var _fullscreenApi = _dereq_(44); 8914 8915var _fullscreenApi2 = _interopRequireDefault(_fullscreenApi); 8916 8917var _mediaError = _dereq_(46); 8918 8919var _mediaError2 = _interopRequireDefault(_mediaError); 8920 8921var _tuple = _dereq_(97); 8922 8923var _tuple2 = _interopRequireDefault(_tuple); 8924 8925var _obj = _dereq_(88); 8926 8927var _mergeOptions = _dereq_(87); 8928 8929var _mergeOptions2 = _interopRequireDefault(_mergeOptions); 8930 8931var _textTrackListConverter = _dereq_(69); 8932 8933var _textTrackListConverter2 = _interopRequireDefault(_textTrackListConverter); 8934 8935var _modalDialog = _dereq_(50); 8936 8937var _modalDialog2 = _interopRequireDefault(_modalDialog); 8938 8939var _tech = _dereq_(62); 8940 8941var _tech2 = _interopRequireDefault(_tech); 8942 8943var _audioTrackList = _dereq_(63); 8944 8945var _audioTrackList2 = _interopRequireDefault(_audioTrackList); 8946 8947var _videoTrackList = _dereq_(76); 8948 8949var _videoTrackList2 = _interopRequireDefault(_videoTrackList); 8950 8951_dereq_(61); 8952 8953_dereq_(59); 8954 8955_dereq_(55); 8956 8957_dereq_(68); 8958 8959_dereq_(45); 8960 8961_dereq_(1); 8962 8963_dereq_(4); 8964 8965_dereq_(8); 8966 8967_dereq_(41); 8968 8969_dereq_(71); 8970 8971_dereq_(60); 8972 8973function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 8974 8975function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 8976 8977function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 8978 8979function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 8980 8981function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
8981onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 8982 * @file player.js 8983 */ 8984// Subclasses Component 8985 8986 8987// The following imports are used only to ensure that the corresponding modules 8988// are always included in the video.js package. Importing the modules will 8989// execute them and they will register themselves with video.js. 8990 8991 8992// Import Html5 tech, at least for disposing the original video tag. 8993 8994 8995// The following tech events are simply re-triggered 8996// on the player when they happen 8997var TECH_EVENTS_RETRIGGER = [ 8998/** 8999 * Fired while the user agent is downloading media data. 9000 * 9001 * @event Player#progress 9002 * @type {EventTarget~Event} 9003 */ 9004/** 9005 * Retrigger the `progress` event that was triggered by the {@link Tech}. 9006 * 9007 * @private 9008 * @method Player#handleTechProgress_ 9009 * @fires Player#progress 9010 * @listens Tech#progress 9011 */ 9012'progress', 9013 9014/** 9015 * Fires when the loading of an audio/video is aborted. 9016 * 9017 * @event Player#abort 9018 * @type {EventTarget~Event} 9019 */ 9020/** 9021 * Retrigger the `abort` event that was triggered by the {@link Tech}. 9022 * 9023 * @private 9024 * @method Player#handleTechAbort_ 9025 * @fires Player#abort 9026 * @listens Tech#abort 9027 */ 9028'abort', 9029 9030/** 9031 * Fires when the browser is intentionally not getting media data. 9032 * 9033 * @event Player#suspend 9034 * @type {EventTarget~Event} 9035 */ 9036/** 9037 * Retrigger the `suspend` event that was triggered by the {@link Tech}. 9038 * 9039 * @private 9040 * @method Player#handleTechSuspend_ 9041 * @fires Player#suspend 9042 * @listens Tech#suspend 9043 */ 9044'suspend', 9045 9046/** 9047 * Fires when the current playlist is empty. 9048 * 9049 * @event Player#emptied 9050 * @type {EventTarget~Event} 9051 */ 9052/** 9053 * Retrigger the `emptied` event that was triggered by the {@link Tech}. 9054 * 9055 * @private 9056 * @method Player#handleTechEmptied_ 9057 * @fires Player#emptied 9058 * @listens Tech#emptied 9059 */ 9060'emptied', 9061/** 9062 * Fires when the browser is trying to get media data, but data is not available. 9063 * 9064 * @event Player#stalled 9065 * @type {EventTarget~Event} 9066 */ 9067/** 9068 * Retrigger the `stalled` event that was triggered by the {@link Tech}. 9069 * 9070 * @private 9071 * @method Player#handleTechStalled_ 9072 * @fires Player#stalled 9073 * @listens Tech#stalled 9074 */ 9075'stalled', 9076 9077/** 9078 * Fires when the browser has loaded meta data for the audio/video. 9079 * 9080 * @event Player#loadedmetadata 9081 * @type {EventTarget~Event} 9082 */ 9083/** 9084 * Retrigger the `stalled` event that was triggered by the {@link Tech}. 9085 * 9086 * @private 9087 * @method Player#handleTechLoadedmetadata_ 9088 * @fires Player#loadedmetadata 9089 * @listens Tech#loadedmetadata 9090 */ 9091'loadedmetadata', 9092 9093/** 9094 * Fires when the browser has loaded the current frame of the audio/video. 9095 * 9096 * @event player#loadeddata 9097 * @type {event} 9098 */ 9099/** 9100 * Retrigger the `loadeddata` event that was triggered by the {@link Tech}. 9101 * 9102 * @private 9103 * @method Player#handleTechLoaddeddata_ 9104 * @fires Player#loadeddata 9105 * @listens Tech#loadeddata 9106 */ 9107'loadeddata', 9108 9109/** 9110 * Fires when the current playback position has changed. 9111 * 9112 * @event player#timeupdate 9113 * @type {event} 9114 */ 9115/** 9116 * Retrigger the `timeupdate` event that was triggered by the {@link Tech}. 9117 * 9118 * @private 9119 * @method Player#handleTechTimeUpdate_ 9120 * @fires Player#timeupdate 9121 * @listens Tech#timeupdate 9122 */ 9123'timeupdate', 9124 9125/** 9126 * Fires when the playing speed of the audio/video is changed 9127 * 9128 * @event player#ratechange 9129 * @type {event} 9130 */ 9131/** 9132 * Retrigger the `ratechange` event that was triggered by the {@link Tech}. 9133 * 9134 * @private 9135 * @method Player#handleTechRatechange_ 9136 * @fires Player#ratechange 9137 * @listens Tech#ratechange 9138 */ 9139'ratechange', 9140 9141/** 9142 * Fires when the volume has been changed 9143 * 9144 * @event player#volumechange 9145 * @type {event} 9146 */ 9147/** 9148 * Retrigger the `volumechange` event that was triggered by the {@link Tech}. 9149 * 9150 * @private 9151 * @method Player#handleTechVolumechange_ 9152 * @fires Player#volumechange 9153 * @listens Tech#volumechange 9154 */ 9155'volumechange', 9156 9157/** 9158 * Fires when the text track has been changed 9159 * 9160 * @event player#texttrackchange 9161 * @type {event} 9162 */ 9163/** 9164 * Retrigger the `texttrackchange` event that was triggered by the {@link Tech}. 9165 * 9166 * @private 9167 * @method Player#handleTechTexttrackchange_ 9168 * @fires Player#texttrackchange 9169 * @listens Tech#texttrackchange 9170 */ 9171'texttrackchange']; 9172 9173/** 9174 * An instance of the `Player` class is created when any of the Video.js setup methods 9175 * are used to initialize a video. 9176 * 9177 * After an instance has been created it can be accessed globally in two ways: 9178 * 1. By calling `videojs('example_video_1');` 9179 * 2. By using it directly via `videojs.players.example_video_1;` 9180 * 9181 * @extends Component 9182 */ 9183 9184var Player = function (_Component) { 9185 _inherits(Player, _Component); 9186 9187 /** 9188 * Create an instance of this class. 9189 * 9190 * @param {Element} tag
9191 * The original video DOM element used for configuring options. 9192 * 9193 * @param {Object} [options] 9194 * Object of option names and values. 9195 * 9196 * @param {Component~ReadyCallback} [ready] 9197 * Ready callback function. 9198 */ 9199 function Player(tag, options, ready) { 9200 _classCallCheck(this, Player); 9201 9202 // Make sure tag ID exists 9203 tag.id = tag.id || 'vjs_video_' + Guid.newGUID(); 9204 9205 // Set Options 9206 // The options argument overrides options set in the video tag 9207 // which overrides globally set options. 9208 // This latter part coincides with the load order 9209 // (tag must exist before Player) 9210 options = (0, _obj.assign)(Player.getTagSettings(tag), options); 9211 9212 // Delay the initialization of children because we need to set up 9213 // player properties first, and can't use `this` before `super()` 9214 options.initChildren = false; 9215 9216 // Same with creating the element 9217 options.createEl = false; 9218 9219 // we don't want the player to report touch activity on itself 9220 // see enableTouchActivity in Component 9221 options.reportTouchActivity = false; 9222 9223 // If language is not set, get the closest lang attribute 9224 if (!options.language) { 9225 if (typeof tag.closest === 'function') { 9226 var closest = tag.closest('[lang]'); 9227 9228 if (closest) { 9229 options.language = closest.getAttribute('lang'); 9230 } 9231 } else { 9232 var element = tag; 9233 9234 while (element && element.nodeType === 1) { 9235 if (Dom.getElAttributes(element).hasOwnProperty('lang')) { 9236 options.language = element.getAttribute('lang'); 9237 break; 9238 } 9239 element = element.parentNode; 9240 } 9241 } 9242 } 9243 9244 // Run base component initializing with new options 9245 9246 // if the global option object was accidentally blown away by 9247 // someone, bail early with an informative error 9248 var _this = _possibleConstructorReturn(this, _Component.call(this, null, options, ready)); 9249 9250 if (!_this.options_ || !_this.options_.techOrder || !_this.options_.techOrder.length) { 9251 throw new Error('No techOrder specified. Did you overwrite ' + 'videojs.options instead of just changing the ' + 'properties you want to override?'); 9252 } 9253 9254 // Store the original tag used to set options 9255 _this.tag = tag; 9256 9257 // Store the tag attributes used to restore html5 element 9258 _this.tagAttributes = tag && Dom.getElAttributes(tag); 9259 9260 // Update current language 9261 _this.language(_this.options_.language); 9262 9263 // Update Supported Languages 9264 if (options.languages) { 9265 // Normalise player option languages to lowercase 9266 var languagesToLower = {}; 9267 9268 Object.getOwnPropertyNames(options.languages).forEach(function (name) { 9269 languagesToLower[name.toLowerCase()] = options.languages[name]; 9270 }); 9271 _this.languages_ = languagesToLower; 9272 } else { 9273 _this.languages_ = Player.prototype.options_.languages; 9274 } 9275 9276 // Cache for video property values. 9277 _this.cache_ = {}; 9278 9279 // Set poster 9280 _this.poster_ = options.poster || ''; 9281 9282 // Set controls 9283 _this.controls_ = !!options.controls; 9284 9285 // Original tag settings stored in options 9286 // now remove immediately so native controls don't flash. 9287 // May be turned back on by HTML5 tech if nativeControlsForTouch is true 9288 tag.controls = false; 9289 9290 /* 9291 * Store the internal state of scrubbing 9292 * 9293 * @private 9294 * @return {Boolean} True if the user is scrubbing 9295 */ 9296 _this.scrubbing_ = false; 9297 9298 _this.el_ = _this.createEl(); 9299 9300 // We also want to pass the original player options to each component and plugin 9301 // as well so they don't need to reach back into the player for options later. 9302 // We also need to do another copy of this.options_ so we don't end up with 9303 // an infinite loop. 9304 var playerOptionsCopy = (0, _mergeOptions2['default'])(_this.options_); 9305 9306 // Load plugins 9307 if (options.plugins) { 9308 var plugins = options.plugins; 9309 9310 Object.getOwnPropertyNames(plugins).forEach(function (name) { 9311 if (typeof this[name] === 'function') { 9312 this[name](plugins[name]); 9313 } else { 9314 _log2['default'].error('Unable to find plugin:', name); 9315 } 9316 }, _this); 9317 } 9318 9319 _this.options_.playerOptions = playerOptionsCopy; 9320 9321 _this.initChildren(); 9322 9323 // Set isAudio based on whether or not an audio tag was used 9324 _this.isAudio(tag.nodeName.toLowerCase() === 'audio'); 9325 9326 // Update controls className. Can't do this when the controls are initially 9327 // set because the element doesn't exist yet. 9328 if (_this.controls()) { 9329 _this.addClass('vjs-controls-enabled'); 9330 } else { 9331 _this.addClass('vjs-controls-disabled'); 9332 } 9333 9334 // Set ARIA label and region role depending on player type 9335 _this.el_.setAttribute('role', 'region'); 9336 if (_this.isAudio()) { 9337 _this.el_.setAttribute('aria-label', 'audio player'); 9338 } else { 9339 _this.el_.setAttribute('aria-label', 'video player'); 9340 } 9341 9342 if (_this.isAudio()) { 9343 _this.addClass('vjs-audio'); 9344 } 9345 9346 if (_this.flexNotSupported_()) { 9347 _this.addClass('vjs-no-flex'); 9348 } 9349 9350 // TODO: Make this smarter. Toggle user state between touching/mousing 9351 // using events, since devices can have both touch and mouse events. 9352 // if (browser.TOUCH_ENABLED) { 9353 // this.addClass('vjs-touch-enabled'); 9354 // } 9355 9356 // iOS Safari has broken hover handling 9357 if (!browser.IS_IOS) { 9358 _this.addClass('vjs-workinghover'); 9359 } 9360 9361 // Make player easily findable by ID 9362 Player.players[_this.id_] = _this; 9363 9364 // Add a major version class to aid css in plugins 9365 var majorVersion = '5.20.2'.split('.')[0]; 9366 9367 _this.addClass('vjs-v' + majorVersion); 9368 9369 // When the player is first initialized, trigger activity so components 9370 // like the control bar show themselves if needed 9371 _this.userActive(true); 9372 _this.reportUserActivity(); 9373 _this.listenForUserActivity_(); 9374 9375 _this.on('fullscreenchange', _this.handleFullscreenChange_); 9376 _this.on('stageclick', _this.handleStageClick_); 9377 return _this; 9378 } 9379 9380 /** 9381 * Destroys the video player and does any necessary cleanup. 9382 * 9383 * This is especially helpful if you are dynamically adding and removing videos 9384 * to/from the DOM. 9385 * 9386 * @fires Player#dispose 9387 */ 9388 9389 9390 Player.prototype.dispose = function dispose() { 9391 /** 9392 * Called when the player is being disposed of. 9393 * 9394 * @event Player#dispose 9395 * @type {EventTarget~Event} 9396 */ 9397 this.trigger('dispose'); 9398 // prevent dispose from being called twice 9399 this.off('dispose'); 9400 9401 if (this.styleEl_ && this.styleEl_.parentNode) { 9402 this.styleEl_.parentNode.removeChild(this.styleEl_); 9403 } 9404 9405 // Kill reference to this player 9406 Player.players[this.id_] = null; 9407 9408 if (this.tag && this.tag.player) { 9409 this.tag.player = null; 9410 } 9411 9412 if (this.el_ && this.el_.player) { 9413 this.el_.player = null; 9414 } 9415 9416 if (this.tech_) { 9417 this.tech_.dispose(); 9418 } 9419 9420 _Component.prototype.dispose.call(this); 9421 }; 9422 9423 /** 9424 * Create the `Player`'s DOM element. 9425 * 9426 * @return {Element} 9427 * The DOM element that gets created. 9428 */ 9429 9430 9431 Player.prototype.createEl = function createEl() { 9432 var tag = this.tag; 9433 var el = void 0; 9434 var playerElIngest = this.playerElIngest_ = tag.parentNode && tag.parentNode.hasAttribute && tag.parentNode.hasAttribute('data-vjs-player'); 9435 9436 if (playerElIngest) { 9437 el = this.el_ = tag.parentNode; 9438 } else { 9439 el = this.el_ = _Component.prototype.createEl.call(this, 'div'); 9440 } 9441 9442 // set tabindex to -1 so we could focus on the player element 9443 tag.setAttribute('tabindex', '-1'); 9444 9445 // Remove width/height attrs from tag so CSS can make it 100% width/height 9446 tag.removeAttribute('width'); 9447 tag.removeAttribute('height'); 9448 9449 // Copy over all the attributes from the tag, including ID and class 9450 // ID will now reference player box, not the video tag 9451 var attrs = Dom.getElAttributes(tag); 9452
9453 Object.getOwnPropertyNames(attrs).forEach(function (attr) { 9454 // workaround so we don't totally break IE7 9455 // http://stackoverflow.com/questions/3653444/css-styles-not-applied-on-dynamic-elements-in-internet-explorer-7 9456 if (attr === 'class') { 9457 el.className += ' ' + attrs[attr]; 9458 } else { 9459 el.setAttribute(attr, attrs[attr]); 9460 } 9461 }); 9462 9463 // Update tag id/class for use as HTML5 playback tech 9464 // Might think we should do this after embedding in container so .vjs-tech class 9465 // doesn't flash 100% width/height, but class only applies with .video-js parent 9466 tag.playerId = tag.id; 9467 tag.id += '_html5_api'; 9468 tag.className = 'vjs-tech'; 9469 9470 // Make player findable on elements 9471 tag.player = el.player = this; 9472 // Default state of video is paused 9473 this.addClass('vjs-paused'); 9474 9475 // Add a style element in the player that we'll use to set the width/height 9476 // of the player in a way that's still overrideable by CSS, just like the 9477 // video element 9478 if (_window2['default'].VIDEOJS_NO_DYNAMIC_STYLE !== true) { 9479 this.styleEl_ = stylesheet.createStyleElement('vjs-styles-dimensions'); 9480 var defaultsStyleEl = Dom.$('.vjs-styles-defaults'); 9481 var head = Dom.$('head'); 9482 9483 head.insertBefore(this.styleEl_, defaultsStyleEl ? defaultsStyleEl.nextSibling : head.firstChild); 9484 } 9485 9486 // Pass in the width/height/aspectRatio options which will update the style el 9487 this.width(this.options_.width); 9488 this.height(this.options_.height); 9489 this.fluid(this.options_.fluid); 9490 this.aspectRatio(this.options_.aspectRatio); 9491 9492 // Hide any links within the video/audio tag, because IE doesn't hide them completely. 9493 var links = tag.getElementsByTagName('a'); 9494 9495 for (var i = 0; i < links.length; i++) { 9496 var linkEl = links.item(i); 9497 9498 Dom.addElClass(linkEl, 'vjs-hidden'); 9499 linkEl.setAttribute('hidden', 'hidden'); 9500 } 9501 9502 // insertElFirst seems to cause the networkState to flicker from 3 to 2, so 9503 // keep track of the original for later so we can know if the source originally failed 9504 tag.initNetworkState_ = tag.networkState; 9505 9506 // Wrap video tag in div (el/box) container 9507 if (tag.parentNode && !playerElIngest) { 9508 tag.parentNode.insertBefore(el, tag); 9509 } 9510 9511 // insert the tag as the first child of the player element 9512 // then manually add it to the children array so that this.addChild 9513 // will work properly for other components 9514 // 9515 // Breaks iPhone, fixed in HTML5 setup. 9516 Dom.insertElFirst(tag, el); 9517 this.children_.unshift(tag); 9518 9519 this.el_ = el; 9520 9521 return el; 9522 }; 9523 9524 /** 9525 * A getter/setter for the `Player`'s width. 9526 * 9527 * @param {number} [value] 9528 * The value to set the `Player's width to. 9529 * 9530 * @return {number} 9531 * The current width of the `Player`. 9532 */ 9533 9534 9535 Player.prototype.width = function width(value) { 9536 return this.dimension('width', value); 9537 }; 9538 9539 /** 9540 * A getter/setter for the `Player`'s height. 9541 * 9542 * @param {number} [value] 9543 * The value to set the `Player's heigth to. 9544 * 9545 * @return {number} 9546 * The current heigth of the `Player`. 9547 */ 9548 9549 9550 Player.prototype.height = function height(value) { 9551 return this.dimension('height', value); 9552 }; 9553 9554 /** 9555 * A getter/setter for the `Player`'s width & height. 9556 * 9557 * @param {string} dimension 9558 * This string can be: 9559 * - 'width' 9560 * - 'height' 9561 * 9562 * @param {number} [value] 9563 * Value for dimension specified in the first argument. 9564 * 9565 * @return {Player|number} 9566 * - Returns itself when setting; method can be chained. 9567 * - The dimension arguments value when getting (width/height). 9568 */ 9569 9570 9571 Player.prototype.dimension = function dimension(_dimension, value) { 9572 var privDimension = _dimension + '_'; 9573 9574 if (value === undefined) { 9575 return this[privDimension] || 0; 9576 } 9577 9578 if (value === '') { 9579 // If an empty string is given, reset the dimension to be automatic 9580 this[privDimension] = undefined; 9581 } else { 9582 var parsedVal = parseFloat(value); 9583 9584 if (isNaN(parsedVal)) { 9585 _log2['default'].error('Improper value "' + value + '" supplied for for ' + _dimension); 9586 return this; 9587 } 9588 9589 this[privDimension] = parsedVal; 9590 } 9591 9592 this.updateStyleEl_(); 9593 return this; 9594 }; 9595 9596 /** 9597 * A getter/setter/toggler for the vjs-fluid `className` on the `Player`. 9598 * 9599 * @param {boolean} [bool] 9600 * - A value of true adds the class. 9601 * - A value of false removes the class. 9602 * - No value will toggle the fluid class. 9603 * 9604 * @return {boolean|undefined} 9605 * - The value of fluid when getting. 9606 * - `undefined` when setting. 9607 */ 9608 9609 9610 Player.prototype.fluid = function fluid(bool) { 9611 if (bool === undefined) { 9612 return !!this.fluid_; 9613 } 9614 9615 this.fluid_ = !!bool; 9616 9617 if (bool) { 9618 this.addClass('vjs-fluid'); 9619 } else { 9620 this.removeClass('vjs-fluid'); 9621 } 9622 9623 this.updateStyleEl_(); 9624 }; 9625 9626 /** 9627 * Get/Set the aspect ratio 9628 * 9629 * @param {string} [ratio] 9630 * Aspect ratio for player 9631 * 9632 * @return {string|undefined} 9633 * returns the current aspect ratio when getting 9634 */ 9635 9636 /** 9637 * A getter/setter for the `Player`'s aspect ratio. 9638 * 9639 * @param {string} [ratio] 9640 * The value to set the `Player's aspect ratio to. 9641 * 9642 * @return {string|undefined} 9643 * - The current aspect ratio of the `Player` when getting. 9644 * - undefined when setting 9645 */ 9646 9647 9648 Player.prototype.aspectRatio = function aspectRatio(ratio) { 9649 if (ratio === undefined) { 9650 return this.aspectRatio_; 9651 } 9652 9653 // Check for width:height format 9654 if (!/^\d+\:\d+$/.test(ratio)) { 9655 throw new Error('Improper value supplied for aspect ratio. The format should be width:height, for example 16:9.'); 9656 } 9657 this.aspectRatio_ = ratio; 9658 9659 // We're assuming if you set an aspect ratio you want fluid mode, 9660 // because in fixed mode you could calculate width and height yourself. 9661 this.fluid(true); 9662 9663 this.updateStyleEl_(); 9664 }; 9665 9666 /** 9667 * Update styles of the `Player` element (height, width and aspect ratio). 9668 * 9669 * @private 9670 * @listens Tech#loadedmetadata 9671 */ 9672 9673 9674 Player.prototype.updateStyleEl_ = function updateStyleEl_() { 9675 if (_window2['default'].VIDEOJS_NO_DYNAMIC_STYLE === true) { 9676 var _width = typeof this.width_ === 'number' ? this.width_ : this.options_.width; 9677 var _height = typeof this.height_ === 'number' ? this.height_ : this.options_.height; 9678 var techEl = this.tech_ && this.tech_.el(); 9679 9680 if (techEl) { 9681 if (_width >= 0) { 9682 techEl.width = _width; 9683 } 9684 if (_height >= 0) {
9685 techEl.height = _height; 9686 } 9687 } 9688 9689 return; 9690 } 9691 9692 var width = void 0; 9693 var height = void 0; 9694 var aspectRatio = void 0; 9695 var idClass = void 0; 9696 9697 // The aspect ratio is either used directly or to calculate width and height. 9698 if (this.aspectRatio_ !== undefined && this.aspectRatio_ !== 'auto') { 9699 // Use any aspectRatio that's been specifically set 9700 aspectRatio = this.aspectRatio_; 9701 } else if (this.videoWidth() > 0) { 9702 // Otherwise try to get the aspect ratio from the video metadata 9703 aspectRatio = this.videoWidth() + ':' + this.videoHeight(); 9704 } else { 9705 // Or use a default. The video element's is 2:1, but 16:9 is more common. 9706 aspectRatio = '16:9'; 9707 } 9708 9709 // Get the ratio as a decimal we can use to calculate dimensions 9710 var ratioParts = aspectRatio.split(':'); 9711 var ratioMultiplier = ratioParts[1] / ratioParts[0]; 9712 9713 if (this.width_ !== undefined) { 9714 // Use any width that's been specifically set 9715 width = this.width_; 9716 } else if (this.height_ !== undefined) { 9717 // Or calulate the width from the aspect ratio if a height has been set 9718 width = this.height_ / ratioMultiplier; 9719 } else { 9720 // Or use the video's metadata, or use the video el's default of 300 9721 width = this.videoWidth() || 300; 9722 } 9723 9724 if (this.height_ !== undefined) { 9725 // Use any height that's been specifically set 9726 height = this.height_; 9727 } else { 9728 // Otherwise calculate the height from the ratio and the width 9729 height = width * ratioMultiplier; 9730 } 9731 9732 // Ensure the CSS class is valid by starting with an alpha character 9733 if (/^[^a-zA-Z]/.test(this.id())) { 9734 idClass = 'dimensions-' + this.id(); 9735 } else { 9736 idClass = this.id() + '-dimensions'; 9737 } 9738 9739 // Ensure the right class is still on the player for the style element 9740 this.addClass(idClass); 9741 9742 stylesheet.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 '); 9743 }; 9744 9745 /** 9746 * Load/Create an instance of playback {@link Tech} including element 9747 * and API methods. Then append the `Tech` element in `Player` as a child. 9748 * 9749 * @param {string} techName 9750 * name of the playback technology 9751 * 9752 * @param {string} source 9753 * video source 9754 * 9755 * @private 9756 */ 9757 9758 9759 Player.prototype.loadTech_ = function loadTech_(techName, source) { 9760 var _this2 = this; 9761 9762 // Pause and remove current playback technology 9763 if (this.tech_) { 9764 this.unloadTech_(); 9765 } 9766 9767 // get rid of the HTML5 video tag as soon as we are using another tech 9768 if (techName !== 'Html5' && this.tag) { 9769 _tech2['default'].getTech('Html5').disposeMediaElement(this.tag); 9770 this.tag.player = null; 9771 this.tag = null; 9772 } 9773 9774 this.techName_ = techName; 9775 9776 // Turn off API access because we're loading a new tech that might load asynchronously 9777 this.isReady_ = false; 9778 9779 // Grab tech-specific options from player options and add source and parent element to use. 9780 var techOptions = (0, _obj.assign)({ 9781 source: source, 9782 'nativeControlsForTouch': this.options_.nativeControlsForTouch, 9783 'playerId': this.id(), 9784 'techId': this.id() + '_' + techName + '_api', 9785 'videoTracks': this.videoTracks_, 9786 'textTracks': this.textTracks_, 9787 'audioTracks': this.audioTracks_, 9788 'autoplay': this.options_.autoplay, 9789 'playsinline': this.options_.playsinline, 9790 'preload': this.options_.preload, 9791 'loop': this.options_.loop, 9792 'muted': this.options_.muted, 9793 'poster': this.poster(), 9794 'language': this.language(), 9795 'playerElIngest': this.playerElIngest_ || false, 9796 'vtt.js': this.options_['vtt.js'] 9797 }, this.options_[techName.toLowerCase()]); 9798 9799 if (this.tag) { 9800 techOptions.tag = this.tag; 9801 } 9802 9803 if (source) { 9804 this.currentType_ = source.type; 9805 9806 if (source.src === this.cache_.src && this.cache_.currentTime > 0) { 9807 techOptions.startTime = this.cache_.currentTime; 9808 } 9809 9810 this.cache_.sources = null; 9811 this.cache_.source = source; 9812 this.cache_.src = source.src; 9813 } 9814 9815 // Initialize tech instance 9816 var TechComponent = _tech2['default'].getTech(techName); 9817
9818 // Support old behavior of techs being registered as components. 9819 // Remove once that deprecated behavior is removed. 9820 if (!TechComponent) { 9821 TechComponent = _component2['default'].getComponent(techName); 9822 } 9823 this.tech_ = new TechComponent(techOptions); 9824 9825 // player.triggerReady is always async, so don't need this to be async 9826 this.tech_.ready(Fn.bind(this, this.handleTechReady_), true); 9827 9828 _textTrackListConverter2['default'].jsonToTextTracks(this.textTracksJson_ || [], this.tech_); 9829 9830 // Listen to all HTML5-defined events and trigger them on the player 9831 TECH_EVENTS_RETRIGGER.forEach(function (event) { 9832 _this2.on(_this2.tech_, event, _this2['handleTech' + (0, _toTitleCase2['default'])(event) + '_']); 9833 }); 9834 this.on(this.tech_, 'loadstart', this.handleTechLoadStart_); 9835 this.on(this.tech_, 'waiting', this.handleTechWaiting_); 9836 this.on(this.tech_, 'canplay', this.handleTechCanPlay_); 9837 this.on(this.tech_, 'canplaythrough', this.handleTechCanPlayThrough_); 9838 this.on(this.tech_, 'playing', this.handleTechPlaying_); 9839 this.on(this.tech_, 'ended', this.handleTechEnded_); 9840 this.on(this.tech_, 'seeking', this.handleTechSeeking_); 9841 this.on(this.tech_, 'seeked', this.handleTechSeeked_); 9842 this.on(this.tech_, 'play', this.handleTechPlay_); 9843 this.on(this.tech_, 'firstplay', this.handleTechFirstPlay_); 9844 this.on(this.tech_, 'pause', this.handleTechPause_); 9845 this.on(this.tech_, 'durationchange', this.handleTechDurationChange_); 9846 this.on(this.tech_, 'fullscreenchange', this.handleTechFullscreenChange_); 9847 this.on(this.tech_, 'error', this.handleTechError_); 9848 this.on(this.tech_, 'loadedmetadata', this.updateStyleEl_); 9849 this.on(this.tech_, 'posterchange', this.handleTechPosterChange_); 9850 this.on(this.tech_, 'textdata', this.handleTechTextData_); 9851 9852 this.usingNativeControls(this.techGet_('controls')); 9853 9854 if (this.controls() && !this.usingNativeControls()) { 9855 this.addTechControlsListeners_(); 9856 } 9857 9858 // Add the tech element in the DOM if it was not already there 9859 // Make sure to not insert the original video element if using Html5 9860 if (this.tech_.el().parentNode !== this.el() && (techName !== 'Html5' || !this.tag)) { 9861 Dom.insertElFirst(this.tech_.el(), this.el()); 9862 } 9863 9864 // Get rid of the original video tag reference after the first tech is loaded 9865 if (this.tag) { 9866 this.tag.player = null; 9867 this.tag = null; 9868 } 9869 }; 9870 9871 /** 9872 * Unload and dispose of the current playback {@link Tech}. 9873 * 9874 * @private 9875 */ 9876 9877 9878 Player.prototype.unloadTech_ = function unloadTech_() { 9879 // Save the current text tracks so that we can reuse the same text tracks with the next tech 9880 this.videoTracks_ = this.videoTracks(); 9881 this.textTracks_ = this.textTracks(); 9882 this.audioTracks_ = this.audioTracks(); 9883 this.textTracksJson_ = _textTrackListConverter2['default'].textTracksToJson(this.tech_); 9884 9885 this.isReady_ = false; 9886 9887 this.tech_.dispose(); 9888 9889 this.tech_ = false;
9890 }; 9891 9892 /** 9893 * Return a reference to the current {@link Tech}, but only if given an object with the 9894 * `IWillNotUseThisInPlugins` property having a true value. This is try and prevent misuse 9895 * of techs by plugins. 9896 * 9897 * @param {Object} safety 9898 * An object that must contain `{IWillNotUseThisInPlugins: true}` 9899 * 9900 * @param {boolean} safety.IWillNotUseThisInPlugins 9901 * Must be set to true or else this function will throw an error. 9902 * 9903 * @return {Tech} 9904 * The Tech 9905 */ 9906 9907 9908 Player.prototype.tech = function tech(safety) { 9909 if (safety && safety.IWillNotUseThisInPlugins) { 9910 return this.tech_; 9911 } 9912 var errorText = '\n Please make sure that you are not using this inside of a plugin.\n To disable this alert and error, please pass in an object with\n `IWillNotUseThisInPlugins` to the `tech` method. See\n https://github.com/videojs/video.js/issues/2617 for more info.\n '; 9913 9914 _window2['default'].alert(errorText); 9915 throw new Error(errorText); 9916 }; 9917 9918 /** 9919 * Set up click and touch listeners for the playback element 9920 * 9921 * - On desktops: a click on the video itself will toggle playback 9922 * - On mobile devices: a click on the video toggles controls 9923 * which is done by toggling the user state between active and 9924 * inactive 9925 * - A tap can signal that a user has become active or has become inactive 9926 * e.g. a quick tap on an iPhone movie should reveal the controls. Another 9927 * quick tap should hide them again (signaling the user is in an inactive 9928 * viewing state) 9929 * - In addition to this, we still want the user to be considered inactive after 9930 * a few seconds of inactivity. 9931 * 9932 * > Note: the only part of iOS interaction we can't mimic with this setup 9933 * is a touch and hold on the video element counting as activity in order to 9934 * keep the controls showing, but that shouldn't be an issue. A touch and hold 9935 * on any controls will still keep the user active 9936 * 9937 * @private 9938 */ 9939 9940 9941 Player.prototype.addTechControlsListeners_ = function addTechControlsListeners_() { 9942 // Make sure to remove all the previous listeners in case we are called multiple times. 9943 this.removeTechControlsListeners_(); 9944 9945 // Some browsers (Chrome & IE) don't trigger a click on a flash swf, but do 9946 // trigger mousedown/up. 9947 // http://stackoverflow.com/questions/1444562/javascript-onclick-event-over-flash-object 9948 // Any touch events are set to block the mousedown event from happening 9949 this.on(this.tech_, 'mousedown', this.handleTechClick_); 9950 9951 // If the controls were hidden we don't want that to change without a tap event 9952 // so we'll check if the controls were already showing before reporting user 9953 // activity 9954 this.on(this.tech_, 'touchstart', this.handleTechTouchStart_); 9955 this.on(this.tech_, 'touchmove', this.handleTechTouchMove_); 9956 this.on(this.tech_, 'touchend', this.handleTechTouchEnd_); 9957 9958 // The tap listener needs to come after the touchend listener because the tap 9959 // listener cancels out any reportedUserActivity when setting userActive(false) 9960 this.on(this.tech_, 'tap', this.handleTechTap_); 9961 }; 9962 9963 /** 9964 * Remove the listeners used for click and tap controls. This is needed for 9965 * toggling to controls disabled, where a tap/touch should do nothing. 9966 * 9967 * @private 9968 */ 9969 9970 9971 Player.prototype.removeTechControlsListeners_ = function removeTechControlsListeners_() { 9972 // We don't want to just use `this.off()` because there might be other needed 9973 // listeners added by techs that extend this. 9974 this.off(this.tech_, 'tap', this.handleTechTap_); 9975 this.off(this.tech_, 'touchstart', this.handleTechTouchStart_); 9976 this.off(this.tech_, 'touchmove', this.handleTechTouchMove_); 9977 this.off(this.tech_, 'touchend', this.handleTechTouchEnd_); 9978 this.off(this.tech_, 'mousedown', this.handleTechClick_); 9979 }; 9980 9981 /** 9982 * Player waits for the tech to be ready 9983 * 9984 * @private 9985 */ 9986 9987 9988 Player.prototype.handleTechReady_ = function handleTechReady_() { 9989 this.triggerReady(); 9990 9991 // Keep the same volume as before 9992 if (this.cache_.volume) { 9993 this.techCall_('setVolume', this.cache_.volume); 9994 } 9995 9996 // Look if the tech found a higher resolution poster while loading 9997 this.handleTechPosterChange_(); 9998 9999 // Update the duration if available 10000 this.handleTechDurationChange_(); 10001 10002 // Chrome and Safari both have issues with autoplay. 10003 // In Safari (5.1.1), when we move the video element into the container div, autoplay doesn't work. 10004 // In Chrome (15), if you have autoplay + a poster + no controls, the video gets hidden (but audio plays) 10005 // This fixes both issues. Need to wait for API, so it updates displays correctly 10006 if ((this.src() || this.currentSrc()) && this.tag && this.options_.autoplay && this.paused()) {
10007 try { 10008 // Chrome Fix. Fixed in Chrome v16. 10009 delete this.tag.poster; 10010 } catch (e) { 10011 (0, _log2['default'])('deleting tag.poster throws in some browsers', e); 10012 } 10013 this.play(); 10014 } 10015 }; 10016 10017 /** 10018 * Retrigger the `loadstart` event that was triggered by the {@link Tech}. This 10019 * function will also trigger {@link Player#firstplay} if it is the first loadstart 10020 * for a video. 10021 * 10022 * @fires Player#loadstart 10023 * @fires Player#firstplay 10024 * @listens Tech#loadstart 10025 * @private 10026 */ 10027 10028 10029 Player.prototype.handleTechLoadStart_ = function handleTechLoadStart_() { 10030 // TODO: Update to use `emptied` event instead. See #1277. 10031 10032 this.removeClass('vjs-ended'); 10033 this.removeClass('vjs-seeking'); 10034 10035 // reset the error state 10036 this.error(null); 10037 10038 // If it's already playing we want to trigger a firstplay event now. 10039 // The firstplay event relies on both the play and loadstart events 10040 // which can happen in any order for a new source 10041 if (!this.paused()) { 10042 /** 10043 * Fired when the user agent begins looking for media data 10044 * 10045 * @event Player#loadstart 10046 * @type {EventTarget~Event} 10047 */ 10048 this.trigger('loadstart'); 10049 this.trigger('firstplay'); 10050 } else { 10051 // reset the hasStarted state 10052 this.hasStarted(false); 10053 this.trigger('loadstart'); 10054 } 10055 }; 10056 10057 /** 10058 * Add/remove the vjs-has-started class 10059 * 10060 * @fires Player#firstplay 10061 * 10062 * @param {boolean} hasStarted 10063 * - true: adds the class 10064 * - false: remove the class 10065 * 10066 * @return {boolean} 10067 * the boolean value of hasStarted 10068 */ 10069 10070 10071 Player.prototype.hasStarted = function hasStarted(_hasStarted) { 10072 if (_hasStarted !== undefined) { 10073 // only update if this is a new value 10074 if (this.hasStarted_ !== _hasStarted) { 10075 this.hasStarted_ = _hasStarted; 10076 if (_hasStarted) { 10077 this.addClass('vjs-has-started'); 10078 // trigger the firstplay event if this newly has played 10079 this.trigger('firstplay'); 10080 } else { 10081 this.removeClass('vjs-has-started'); 10082 } 10083 } 10084 return this; 10085 } 10086 return !!this.hasStarted_; 10087 }; 10088 10089 /** 10090 * Fired whenever the media begins or resumes playback 10091 * 10092 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-play} 10093 * @fires Player#play 10094 * @listens Tech#play 10095 * @private 10096 */ 10097 10098 10099 Player.prototype.handleTechPlay_ = function handleTechPlay_() { 10100 this.removeClass('vjs-ended'); 10101 this.removeClass('vjs-paused'); 10102 this.addClass('vjs-playing'); 10103 10104 // hide the poster when the user hits play 10105 this.hasStarted(true); 10106 /** 10107 * Triggered whenever an {@link Tech#play} event happens. Indicates that 10108 * playback has started or resumed. 10109 * 10110 * @event Player#play 10111 * @type {EventTarget~Event} 10112 */ 10113 this.trigger('play'); 10114 }; 10115 10116 /** 10117 * Retrigger the `waiting` event that was triggered by the {@link Tech}. 10118 * 10119 * @fires Player#waiting 10120 * @listens Tech#waiting 10121 * @private 10122 */ 10123 10124 10125 Player.prototype.handleTechWaiting_ = function handleTechWaiting_() { 10126 var _this3 = this; 10127 10128 this.addClass('vjs-waiting'); 10129 /** 10130 * A readyState change on the DOM element has caused playback to stop. 10131 * 10132 * @event Player#waiting 10133 * @type {EventTarget~Event} 10134 */ 10135 this.trigger('waiting'); 10136 this.one('timeupdate', function () { 10137 return _this3.removeClass('vjs-waiting'); 10138 }); 10139 }; 10140 10141 /** 10142 * Retrigger the `canplay` event that was triggered by the {@link Tech}. 10143 * > Note: This is not consistent between browsers. See #1351 10144 * 10145 * @fires Player#canplay 10146 * @listens Tech#canplay 10147 * @private 10148 */ 10149 10150 10151 Player.prototype.handleTechCanPlay_ = function handleTechCanPlay_() { 10152 this.removeClass('vjs-waiting'); 10153 /** 10154 * The media has a readyState of HAVE_FUTURE_DATA or greater. 10155 * 10156 * @event Player#canplay 10157 * @type {EventTarget~Event} 10158 */ 10159 this.trigger('canplay'); 10160 }; 10161 10162 /** 10163 * Retrigger the `canplaythrough` event that was triggered by the {@link Tech}. 10164 * 10165 * @fires Player#canplaythrough 10166 * @listens Tech#canplaythrough 10167 * @private 10168 */ 10169 10170 10171 Player.prototype.handleTechCanPlayThrough_ = function handleTechCanPlayThrough_() { 10172 this.removeClass('vjs-waiting'); 10173 /** 10174 * The media has a readyState of HAVE_ENOUGH_DATA or greater. This means that the 10175 * entire media file can be played without buffering. 10176 * 10177 * @event Player#canplaythrough 10178 * @type {EventTarget~Event} 10179 */ 10180 this.trigger('canplaythrough'); 10181 }; 10182 10183 /** 10184 * Retrigger the `playing` event that was triggered by the {@link Tech}. 10185 * 10186 * @fires Player#playing 10187 * @listens Tech#playing 10188 * @private 10189 */ 10190 10191 10192 Player.prototype.handleTechPlaying_ = function handleTechPlaying_() { 10193 this.removeClass('vjs-waiting'); 10194 /** 10195 * The media is no longer blocked from playback, and has started playing. 10196 * 10197 * @event Player#playing 10198 * @type {EventTarget~Event} 10199 */ 10200 this.trigger('playing'); 10201 }; 10202 10203 /** 10204 * Retrigger the `seeking` event that was triggered by the {@link Tech}. 10205 * 10206 * @fires Player#seeking 10207 * @listens Tech#seeking 10208 * @private 10209 */ 10210 10211 10212 Player.prototype.handleTechSeeking_ = function handleTechSeeking_() { 10213 this.addClass('vjs-seeking'); 10214 /** 10215 * Fired whenever the player is jumping to a new time 10216 * 10217 * @event Player#seeking 10218 * @type {EventTarget~Event} 10219 */ 10220 this.trigger('seeking'); 10221 }; 10222 10223 /** 10224 * Retrigger the `seeked` event that was triggered by the {@link Tech}. 10225 * 10226 * @fires Player#seeked 10227 * @listens Tech#seeked 10228 * @private 10229 */ 10230 10231 10232 Player.prototype.handleTechSeeked_ = function handleTechSeeked_() { 10233 this.removeClass('vjs-seeking'); 10234 /** 10235 * Fired when the player has finished jumping to a new time 10236 * 10237 * @event Player#seeked 10238 * @type {EventTarget~Event} 10239 */ 10240 this.trigger('seeked'); 10241 }; 10242 10243 /** 10244 * Retrigger the `firstplay` event that was triggered by the {@link Tech}. 10245 * 10246 * @fires Player#firstplay 10247 * @listens Tech#firstplay 10248 * @deprecated As of 6.0 passing the `starttime` option to the player will be deprecated 10249 * @private 10250 */ 10251 10252 10253 Player.prototype.handleTechFirstPlay_ = function handleTechFirstPlay_() { 10254 // If the first starttime attribute is specified 10255 // then we will start at the given offset in seconds 10256 if (this.options_.starttime) { 10257 _log2['default'].warn('Passing the `starttime` option to the player will be deprecated
vendor: 6,466 bytes, lines 10257-10547
10257in 6.0'); 10258 this.currentTime(this.options_.starttime); 10259 } 10260 10261 this.addClass('vjs-has-started'); 10262 /** 10263 * Fired the first time a video is played. Not part of the HLS spec, and this is 10264 * probably not the best implementation yet, so use sparingly. If you don't have a 10265 * reason to prevent playback, use `myPlayer.one('play');` instead. 10266 * 10267 * @event Player#firstplay 10268 * @type {EventTarget~Event} 10269 */ 10270 this.trigger('firstplay'); 10271 }; 10272 10273 /** 10274 * Retrigger the `pause` event that was triggered by the {@link Tech}. 10275 * 10276 * @fires Player#pause 10277 * @listens Tech#pause 10278 * @private 10279 */ 10280 10281 10282 Player.prototype.handleTechPause_ = function handleTechPause_() { 10283 this.removeClass('vjs-playing'); 10284 this.addClass('vjs-paused'); 10285 /** 10286 * Fired whenever the media has been paused 10287 * 10288 * @event Player#pause 10289 * @type {EventTarget~Event} 10290 */ 10291 this.trigger('pause'); 10292 }; 10293 10294 /** 10295 * Retrigger the `ended` event that was triggered by the {@link Tech}. 10296 * 10297 * @fires Player#ended 10298 * @listens Tech#ended 10299 * @private 10300 */ 10301 10302 10303 Player.prototype.handleTechEnded_ = function handleTechEnded_() { 10304 this.addClass('vjs-ended'); 10305 if (this.options_.loop) { 10306 this.currentTime(0); 10307 this.play(); 10308 } else if (!this.paused()) { 10309 this.pause(); 10310 } 10311 10312 /** 10313 * Fired when the end of the media resource is reached (currentTime == duration) 10314 * 10315 * @event Player#ended 10316 * @type {EventTarget~Event} 10317 */ 10318 this.trigger('ended'); 10319 }; 10320 10321 /** 10322 * Fired when the duration of the media resource is first known or changed 10323 * 10324 * @listens Tech#durationchange 10325 * @private 10326 */ 10327 10328 10329 Player.prototype.handleTechDurationChange_ = function handleTechDurationChange_() { 10330 this.duration(this.techGet_('duration')); 10331 }; 10332 10333 /** 10334 * Handle a click on the media element to play/pause 10335 * 10336 * @param {EventTarget~Event} event 10337 * the event that caused this function to trigger 10338 * 10339 * @listens Tech#mousedown 10340 * @private 10341 */ 10342 10343 10344 Player.prototype.handleTechClick_ = function handleTechClick_(event) { 10345 // We're using mousedown to detect clicks thanks to Flash, but mousedown 10346 // will also be triggered with right-clicks, so we need to prevent that 10347 if (event.button !== 0) { 10348 return; 10349 } 10350 10351 // When controls are disabled a click should not toggle playback because 10352 // the click is considered a control 10353 if (this.controls()) { 10354 if (this.paused()) { 10355 this.play(); 10356 } else { 10357 this.pause(); 10358 } 10359 } 10360 }; 10361 10362 /** 10363 * Handle a tap on the media element. It will toggle the user 10364 * activity state, which hides and shows the controls. 10365 * 10366 * @listens Tech#tap 10367 * @private 10368 */ 10369 10370 10371 Player.prototype.handleTechTap_ = function handleTechTap_() { 10372 this.userActive(!this.userActive()); 10373 }; 10374 10375 /** 10376 * Handle touch to start 10377 * 10378 * @listens Tech#touchstart 10379 * @private 10380 */ 10381 10382 10383 Player.prototype.handleTechTouchStart_ = function handleTechTouchStart_() { 10384 this.userWasActive = this.userActive(); 10385 }; 10386 10387 /** 10388 * Handle touch to move 10389 * 10390 * @listens Tech#touchmove 10391 * @private 10392 */ 10393 10394 10395 Player.prototype.handleTechTouchMove_ = function handleTechTouchMove_() { 10396 if (this.userWasActive) { 10397 this.reportUserActivity(); 10398 } 10399 }; 10400 10401 /** 10402 * Handle touch to end 10403 * 10404 * @param {EventTarget~Event} event 10405 * the touchend event that triggered 10406 * this function 10407 * 10408 * @listens Tech#touchend 10409 * @private 10410 */ 10411 10412 10413 Player.prototype.handleTechTouchEnd_ = function handleTechTouchEnd_(event) { 10414 // Stop the mouse events from also happening 10415 event.preventDefault(); 10416 }; 10417 10418 /** 10419 * Fired when the player switches in or out of fullscreen mode 10420 * 10421 * @private 10422 * @listens Player#fullscreenchange 10423 */ 10424 10425 10426 Player.prototype.handleFullscreenChange_ = function handleFullscreenChange_() { 10427 if (this.isFullscreen()) { 10428 this.addClass('vjs-fullscreen'); 10429 } else { 10430 this.removeClass('vjs-fullscreen'); 10431 } 10432 }; 10433 10434 /** 10435 * native click events on the SWF aren't triggered on IE11, Win8.1RT 10436 * use stageclick events triggered from inside the SWF instead 10437 * 10438 * @private 10439 * @listens stageclick 10440 */ 10441 10442 10443 Player.prototype.handleStageClick_ = function handleStageClick_() { 10444 this.reportUserActivity(); 10445 }; 10446 10447 /** 10448 * Handle Tech Fullscreen Change 10449 * 10450 * @param {EventTarget~Event} event 10451 * the fullscreenchange event that triggered this function 10452 * 10453 * @param {Object} data 10454 * the data that was sent with the event 10455 * 10456 * @private 10457 * @listens Tech#fullscreenchange 10458 * @fires Player#fullscreenchange 10459 */ 10460 10461 10462 Player.prototype.handleTechFullscreenChange_ = function handleTechFullscreenChange_(event, data) { 10463 if (data) { 10464 this.isFullscreen(data.isFullscreen); 10465 } 10466 /** 10467 * Fired when going in and out of fullscreen. 10468 * 10469 * @event Player#fullscreenchange 10470 * @type {EventTarget~Event} 10471 */ 10472 this.trigger('fullscreenchange'); 10473 }; 10474 10475 /** 10476 * Fires when an error occurred during the loading of an audio/video. 10477 * 10478 * @private 10479 * @listens Tech#error 10480 */ 10481 10482 10483 Player.prototype.handleTechError_ = function handleTechError_() { 10484 var error = this.tech_.error(); 10485 10486 this.error(error); 10487 }; 10488 10489 /** 10490 * Retrigger the `textdata` event that was triggered by the {@link Tech}. 10491 * 10492 * @fires Player#textdata 10493 * @listens Tech#textdata 10494 * @private 10495 */ 10496 10497 10498 Player.prototype.handleTechTextData_ = function handleTechTextData_() { 10499 var data = null; 10500 10501 if (arguments.length > 1) { 10502 data = arguments[1]; 10503 } 10504 10505 /** 10506 * Fires when we get a textdata event from tech 10507 * 10508 * @event Player#textdata 10509 * @type {EventTarget~Event} 10510 */ 10511 this.trigger('textdata', data); 10512 }; 10513 10514 /** 10515 * Get object for cached values. 10516 * 10517 * @return {Object} 10518 * get the current object cache 10519 */ 10520 10521 10522 Player.prototype.getCache = function getCache() { 10523 return this.cache_; 10524 }; 10525 10526 /** 10527 * Pass values to the playback tech 10528 * 10529 * @param {string} [method] 10530 * the method to call 10531 * 10532 * @param {Object} arg 10533 * the argument to pass 10534 * 10535 * @private 10536 */ 10537 10538 10539 Player.prototype.techCall_ = function techCall_(method, arg) { 10540 // If it's not ready yet, call method when it is 10541 if (this.tech_ && !this.tech_.isReady_) { 10542 this.tech_.ready(function () { 10543 this[method](arg); 10544 }, true); 10545 10546 // Otherwise call method now 10547 } else {
10548 try { 10549 if (this.tech_) { 10550 this.tech_[method](arg); 10551 } 10552 } catch (e) { 10553 (0, _log2['default'])(e); 10554 throw e; 10555 } 10556 } 10557 }; 10558 10559 /** 10560 * Get calls can't wait for the tech, and sometimes don't need to. 10561 * 10562 * @param {string} method 10563 * Tech method 10564 * 10565 * @return {Function|undefined} 10566 * the method or undefined 10567 * 10568 * @private 10569 */ 10570 10571 10572 Player.prototype.techGet_ = function techGet_(method) { 10573 if (this.tech_ && this.tech_.isReady_) { 10574 10575 // Flash likes to die and reload when you hide or reposition it. 10576 // In these cases the object methods go away and we get errors. 10577 // When that happens we'll catch the errors and inform tech that it's not ready any more. 10578 try { 10579 return this.tech_[method](); 10580 } catch (e) { 10581 // When building additional tech libs, an expected method may not be defined yet 10582 if (this.tech_[method] === undefined) { 10583 (0, _log2['default'])('Video.js: ' + method + ' method not defined for ' + this.techName_ + ' playback technology.', e); 10584 10585 // When a method isn't available on the object it throws a TypeError 10586 } else if (e.name === 'TypeError') { 10587 (0, _log2['default'])('Video.js: ' + method + ' unavailable on ' + this.techName_ + ' playback technology element.', e); 10588 this.tech_.isReady_ = false; 10589 } else { 10590 (0, _log2['default'])(e); 10591 } 10592 throw e; 10593 } 10594 } 10595 10596 return; 10597 }; 10598 10599 /** 10600 * start media playback 10601 * 10602 * @return {Player} 10603 * A reference to the player object this function was called on 10604 */ 10605 10606 10607 Player.prototype.play = function play() { 10608 // Only calls the tech's play if we already have a src loaded 10609 if (this.src() || this.currentSrc()) { 10610 this.techCall_('play'); 10611 } else { 10612 this.tech_.one('loadstart', function () { 10613 this.play(); 10614 }); 10615 } 10616 10617 return this; 10618 }; 10619 10620 /** 10621 * Pause the video playback 10622 * 10623 * @return {Player} 10624 * A reference to the player object this function was called on 10625 */ 10626 10627 10628 Player.prototype.pause = function pause() { 10629 this.techCall_('pause'); 10630 return this; 10631 }; 10632 10633 /** 10634 * Check if the player is paused or has yet to play 10635 * 10636 * @return {boolean} 10637 * - false: if the media is currently playing 10638 * - true: if media is not currently playing 10639 */ 10640 10641 10642 Player.prototype.paused = function paused() { 10643 // The initial state of paused should be true (in Safari it's actually false) 10644 return this.techGet_('paused') === false ? false : true; 10645 }; 10646 10647 /** 10648 * Returns whether or not the user is "scrubbing". Scrubbing is 10649 * when the user has clicked the progress bar handle and is 10650 * dragging it along the progress bar. 10651 * 10652 * @param {boolean} [isScrubbing] 10653 * wether the user is or is not scrubbing 10654 * 10655 * @return {boolean|Player} 10656 * A instance of the player that called this function when setting, 10657 * and the value of scrubbing when getting 10658 */ 10659 10660 10661 Player.prototype.scrubbing = function scrubbing(isScrubbing) { 10662 if (isScrubbing !== undefined) { 10663 this.scrubbing_ = !!isScrubbing; 10664 10665 if (isScrubbing) { 10666 this.addClass('vjs-scrubbing'); 10667 } else { 10668 this.removeClass('vjs-scrubbing'); 10669 } 10670 10671 return this; 10672 } 10673 10674 return this.scrubbing_; 10675 }; 10676 10677 /** 10678 * Get or set the current time (in seconds) 10679 * 10680 * @param {number|string} [seconds] 10681 * The time to seek to in seconds 10682 * 10683 * @return {Player|number} 10684 * - the current time in seconds when getting 10685 * - a reference to the current player object when setting 10686 */ 10687 10688 10689 Player.prototype.currentTime = function currentTime(seconds) { 10690 if (seconds !== undefined) { 10691 10692 this.techCall_('setCurrentTime', seconds); 10693 10694 return this; 10695 } 10696 10697 // cache last currentTime and return. default to 0 seconds 10698 // 10699 // Caching the currentTime is meant to prevent a massive amount of reads on the tech's 10700 // currentTime when scrubbing, but may not provide much performance benefit afterall. 10701 // Should be tested. Also something has to read the actual current time or the cache will 10702 // never get updated. 10703 this.cache_.currentTime = this.techGet_('currentTime') || 0; 10704 return this.cache_.currentTime; 10705 }; 10706 10707 /** 10708 * Normally gets the length in time of the video in seconds; 10709 * in all but the rarest use cases an argument will NOT be passed to the method 10710 * 10711 * > **NOTE**: The video must have started loading before the duration can be 10712 * known, and in the case of Flash, may not be known until the video starts 10713 * playing. 10714 * 10715 * @fires Player#durationchange 10716 * 10717 * @param {number} [seconds] 10718 * The duration of the video to set in seconds 10719 * 10720 * @return {number|Player} 10721 * - The duration of the video in seconds when getting 10722 * - A reference to the player that called this function 10723 * when setting 10724 */ 10725 10726 10727 Player.prototype.duration = function duration(seconds) { 10728 if (seconds === undefined) { 10729 // return NaN if the duration is not known 10730 return this.cache_.duration !== undefined ? this.cache_.duration : NaN; 10731 } 10732 10733 seconds = parseFloat(seconds); 10734 10735 // Standardize on Inifity for signaling video is live 10736 if (seconds < 0) { 10737 seconds = Infinity; 10738 } 10739 10740 if (seconds !== this.cache_.duration) { 10741 // Cache the last set value for optimized scrubbing (esp. Flash) 10742 this.cache_.duration = seconds; 10743 10744 if (seconds === Infinity) { 10745 this.addClass('vjs-live'); 10746 } else { 10747 this.removeClass('vjs-live'); 10748 } 10749 /** 10750 * @event Player#durationchange 10751 * @type {EventTarget~Event} 10752 */ 10753 this.trigger('durationchange'); 10754 } 10755 10756 return this; 10757 }; 10758 10759 /** 10760 * Calculates how much time is left in the video. Not part 10761 * of the native video API. 10762 * 10763 * @return {number} 10764 * The time remaining in seconds 10765 */ 10766 10767 10768 Player.prototype.remainingTime = function remainingTime() { 10769 return this.duration() - this.currentTime(); 10770 }; 10771 10772 // 10773 // Kind of like an array of portions of the video that have been downloaded.
10774 10775 /** 10776 * Get a TimeRange object with an array of the times of the video 10777 * that have been downloaded. If you just want the percent of the 10778 * video that's been downloaded, use bufferedPercent. 10779 * 10780 * @see [Buffered Spec]{@link http://dev.w3.org/html5/spec/video.html#dom-media-buffered} 10781 * 10782 * @return {TimeRange} 10783 * A mock TimeRange object (following HTML spec) 10784 */ 10785 10786 10787 Player.prototype.buffered = function buffered() { 10788 var buffered = this.techGet_('buffered'); 10789 10790 if (!buffered || !buffered.length) { 10791 buffered = (0, _timeRanges.createTimeRange)(0, 0); 10792 } 10793 10794 return buffered; 10795 }; 10796 10797 /** 10798 * Get the percent (as a decimal) of the video that's been downloaded. 10799 * This method is not a part of the native HTML video API. 10800 * 10801 * @return {number} 10802 * A decimal between 0 and 1 representing the percent 10803 * that is bufferred 0 being 0% and 1 being 100% 10804 */ 10805 10806 10807 Player.prototype.bufferedPercent = function bufferedPercent() { 10808 return (0, _buffer.bufferedPercent)(this.buffered(), this.duration()); 10809 }; 10810 10811 /** 10812 * Get the ending time of the last buffered time range 10813 * This is used in the progress bar to encapsulate all time ranges. 10814 * 10815 * @return {number} 10816 * The end of the last buffered time range 10817 */ 10818 10819 10820 Player.prototype.bufferedEnd = function bufferedEnd() { 10821 var buffered = this.buffered(); 10822 var duration = this.duration(); 10823 var end = buffered.end(buffered.length - 1); 10824 10825 if (end > duration) { 10826 end = duration; 10827 } 10828 10829 return end; 10830 }; 10831 10832 /** 10833 * Get or set the current volume of the media 10834 * 10835 * @param {number} [percentAsDecimal] 10836 * The new volume as a decimal percent: 10837 * - 0 is muted/0%/off 10838 * - 1.0 is 100%/full 10839 * - 0.5 is half volume or 50% 10840 * 10841 * @return {Player|number} 10842 * a reference to the calling player when setting and the 10843 * current volume as a percent when getting 10844 */ 10845 10846 10847 Player.prototype.volume = function volume(percentAsDecimal) { 10848 var vol = void 0; 10849 10850 if (percentAsDecimal !== undefined) { 10851 // Force value to between 0 and 1 10852 vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal))); 10853 this.cache_.volume = vol; 10854 this.techCall_('setVolume', vol); 10855 10856 return this; 10857 } 10858 10859 // Default to 1 when returning current volume. 10860 vol = parseFloat(this.techGet_('volume')); 10861 return isNaN(vol) ? 1 : vol; 10862 }; 10863 10864 /** 10865 * Get the current muted state, or turn mute on or off 10866 * 10867 * @param {boolean} [muted] 10868 * - true to mute 10869 * - false to unmute 10870 * 10871 * @return {boolean|Player} 10872 * - true if mute is on and getting 10873 * - false if mute is off and getting 10874 * - A reference to the current player when setting 10875 */ 10876 10877 10878 Player.prototype.muted = function muted(_muted) { 10879 if (_muted !== undefined) { 10880 this.techCall_('setMuted', _muted); 10881 return this; 10882 } 10883 return this.techGet_('muted') || false; 10884 }; 10885 10886 /** 10887 * Check if current tech can support native fullscreen 10888 * (e.g. with built in controls like iOS, so not our flash swf) 10889 * 10890 * @return {boolean} 10891 * if native fullscreen is supported 10892 */ 10893 10894 10895 Player.prototype.supportsFullScreen = function supportsFullScreen() { 10896 return this.techGet_('supportsFullScreen') || false; 10897 }; 10898 10899 /** 10900 * Check if the player is in fullscreen mode or tell the player that it 10901 * is or is not in fullscreen mode. 10902 * 10903 * > NOTE: As of the latest HTML5 spec, isFullscreen is no longer an official 10904 * property and instead document.fullscreenElement is used. But isFullscreen is 10905 * still a valuable property for internal player workings. 10906 * 10907 * @param {boolean} [isFS] 10908 * Set the players current fullscreen state 10909 * 10910 * @return {boolean|Player} 10911 * - true if fullscreen is on and getting 10912 * - false if fullscreen is off and getting 10913 * - A reference to the current player when setting 10914 */ 10915 10916 10917 Player.prototype.isFullscreen = function isFullscreen(isFS) { 10918 if (isFS !== undefined) { 10919 this.isFullscreen_ = !!isFS; 10920 return this; 10921 } 10922 return !!this.isFullscreen_; 10923 }; 10924 10925 /** 10926 * Increase the size of the video to full screen 10927 * In some browsers, full screen is not supported natively, so it enters 10928 * "full window mode", where the video fills the browser window. 10929 * In browsers and devices that support native full screen, sometimes the 10930 * browser's default controls will be shown, and not the Video.js custom skin. 10931 * This includes most mobile devices (iOS, Android) and older versions of 10932 * Safari. 10933 * 10934 * @fires Player#fullscreenchange 10935 * @return {Player} 10936 * A reference to the current player 10937 */ 10938 10939 10940 Player.prototype.requestFullscreen = function requestFullscreen() { 10941 var fsApi = _fullscreenApi2['default']; 10942 10943 this.isFullscreen(true); 10944 10945 if (fsApi.requestFullscreen) { 10946 // the browser supports going fullscreen at the element level so we can 10947 // take the controls fullscreen as well as the video 10948 10949 // Trigger fullscreenchange event after change 10950 // We have to specifically add this each time, and remove 10951 // when canceling fullscreen. Otherwise if there's multiple 10952 // players on a page, they would all be reacting to the same fullscreen 10953 // events 10954 Events.on(_document2['default'], fsApi.fullscreenchange, Fn.bind(this, function documentFullscreenChange(e) { 10955 this.isFullscreen(_document2['default'][fsApi.fullscreenElement]); 10956 10957 // If cancelling fullscreen, remove event listener. 10958 if (this.isFullscreen() === false) { 10959 Events.off(_document2['default'], fsApi.fullscreenchange, documentFullscreenChange); 10960 } 10961 /** 10962 * @event Player#fullscreenchange 10963 * @type {EventTarget~Event} 10964 */ 10965 this.trigger('fullscreenchange'); 10966 })); 10967 10968 this.el_[fsApi.requestFullscreen](); 10969 } else if (this.tech_.supportsFullScreen()) { 10970 // we can't take the video.js controls fullscreen but we can go fullscreen 10971 // with native controls 10972 this.techCall_('enterFullScreen'); 10973 } else { 10974 // fullscreen isn't supported so we'll just stretch the video element to 10975 // fill the viewport 10976 this.enterFullWindow(); 10977 /** 10978 * @event Player#fullscreenchange 10979 * @type {EventTarget~Event} 10980 */ 10981 this.trigger('fullscreenchange'); 10982 } 10983 10984 return this; 10985 }; 10986 10987 /** 10988 * Return the video to its normal size after having been in full screen mode 10989 * 10990 * @fires Player#fullscreenchange 10991 * 10992 * @return {Player} 10993 * A reference to the current player 10994 */ 10995 10996 10997 Player.prototype.exitFullscreen = function exitFullscreen() { 10998 var fsApi = _fullscreenApi2['default']; 10999 11000 this.isFullscreen(false); 11001 11002 // Check for browser element fullscreen support 11003 if (fsApi.requestFullscreen) { 11004 _document2['default'][fsApi.exitFullscreen](); 11005 } else if (this.tech_.supportsFullScreen()) { 11006 this.techCall_('exitFullScreen'); 11007 } else { 11008 this.exitFullWindow(); 11009 /** 11010 * @event Player#fullscreenchange 11011 * @type {EventTarget~Event} 11012 */ 11013 this.trigger('fullscreenchange'); 11014 } 11015 11016 return this; 11017 }; 11018 11019 /** 11020 * When fullscreen isn't supported we can stretch the 11021 * video container to as wide as the browser will let us. 11022 * 11023 * @fires Player#enterFullWindow 11024 */ 11025 11026 11027 Player.prototype.enterFullWindow = function enterFullWindow() { 11028 this.isFullWindow = true; 11029 11030 // Storing original doc overflow value to return to when fullscreen is off 11031 this.docOrigOverflow = _document2['default'].documentElement.style.overflow; 11032 11033 // Add listener for esc key to exit fullscreen 11034 Events.on(_document2['default'], 'keydown', Fn.bind(this, this.fullWindowOnEscKey)); 11035 11036 // Hide any scroll bars 11037 _document2['default'].documentElement.style.overflow = 'hidden'; 11038 11039 // Apply fullscreen styles 11040 Dom.addElClass(_document2['default'].body, 'vjs-full-window'); 11041 11042 /** 11043 * @event Player#enterFullWindow 11044 * @type {EventTarget~Event} 11045 */ 11046 this.trigger('enterFullWindow'); 11047 }; 11048 11049 /** 11050 * Check for call to either exit full window or 11051 * full screen on ESC key 11052 * 11053 * @param {string} event 11054 * Event to check for key press 11055 */ 11056 11057 11058 Player.prototype.fullWindowOnEscKey = function fullWindowOnEscKey(event) { 11059 if (event.keyCode === 27) { 11060 if (this.isFullscreen() === true) { 11061 this.exitFullscreen(); 11062 } else { 11063 this.exitFullWindow(); 11064 } 11065 } 11066 }; 11067 11068 /** 11069 * Exit full window 11070 * 11071 * @fires Player#exitFullWindow 11072 */ 11073 11074 11075 Player.prototype.exitFullWindow = function exitFullWindow() { 11076 this.isFullWindow = false;
11077 Events.off(_document2['default'], 'keydown', this.fullWindowOnEscKey); 11078 11079 // Unhide scroll bars. 11080 _document2['default'].documentElement.style.overflow = this.docOrigOverflow; 11081 11082 // Remove fullscreen styles 11083 Dom.removeElClass(_document2['default'].body, 'vjs-full-window'); 11084 11085 // Resize the box, controller, and poster to original sizes 11086 // this.positionAll(); 11087 /** 11088 * @event Player#exitFullWindow 11089 * @type {EventTarget~Event} 11090 */ 11091 this.trigger('exitFullWindow'); 11092 }; 11093 11094 /** 11095 * Check whether the player can play a given mimetype 11096 * 11097 * @see https://www.w3.org/TR/2011/WD-html5-20110113/video.html#dom-navigator-canplaytype 11098 * 11099 * @param {string} type 11100 * The mimetype to check 11101 * 11102 * @return {string} 11103 * 'probably', 'maybe', or '' (empty string) 11104 */ 11105 11106 11107 Player.prototype.canPlayType = function canPlayType(type) { 11108 var can = void 0; 11109 11110 // Loop through each playback technology in the options order 11111 for (var i = 0, j = this.options_.techOrder; i < j.length; i++) { 11112 var techName = (0, _toTitleCase2['default'])(j[i]); 11113 var tech = _tech2['default'].getTech(techName); 11114 11115 // Support old behavior of techs being registered as components. 11116 // Remove once that deprecated behavior is removed. 11117 if (!tech) { 11118 tech = _component2['default'].getComponent(techName); 11119 } 11120 11121 // Check if the current tech is defined before continuing 11122 if (!tech) { 11123 _log2['default'].error('The "' + techName + '" tech is undefined. Skipped browser support check for that tech.'); 11124 continue; 11125 } 11126 11127 // Check if the browser supports this technology 11128 if (tech.isSupported()) { 11129 can = tech.canPlayType(type); 11130 11131 if (can) { 11132 return can; 11133 } 11134 } 11135 } 11136 11137 return ''; 11138 }; 11139 11140 /** 11141 * Select source based on tech-order or source-order 11142 * Uses source-order selection if `options.sourceOrder` is truthy. Otherwise, 11143 * defaults to tech-order selection 11144 * 11145 * @param {Array} sources 11146 * The sources for a media asset 11147 * 11148 * @return {Object|boolean} 11149 * Object of source and tech order or false 11150 */ 11151 11152 11153 Player.prototype.selectSource = function selectSource(sources) { 11154 var _this4 = this; 11155 11156 // Get only the techs specified in `techOrder` that exist and are supported by the 11157 // current platform 11158 var techs = this.options_.techOrder.map(_toTitleCase2['default']).map(function (techName) { 11159 // `Component.getComponent(...)` is for support of old behavior of techs 11160 // being registered as components. 11161 // Remove once that deprecated behavior is removed. 11162 return [techName, _tech2['default'].getTech(techName) || _component2['default'].getComponent(techName)]; 11163 }).filter(function (_ref) { 11164 var techName = _ref[0], 11165 tech = _ref[1]; 11166 11167 // Check if the current tech is defined before continuing 11168 if (tech) { 11169 // Check if the browser supports this technology 11170 return tech.isSupported(); 11171 } 11172 11173 _log2['default'].error('The "' + techName + '" tech is undefined. Skipped browser support check for that tech.'); 11174 return false; 11175 }); 11176 11177 // Iterate over each `innerArray` element once per `outerArray` element and execute 11178 // `tester` with both. If `tester` returns a non-falsy value, exit early and return 11179 // that value. 11180 var findFirstPassingTechSourcePair = function findFirstPassingTechSourcePair(outerArray, innerArray, tester) { 11181 var found = void 0; 11182 11183 outerArray.some(function (outerChoice) { 11184 return innerArray.some(function (innerChoice) { 11185 found = tester(outerChoice, innerChoice); 11186 11187 if (found) { 11188 return true; 11189 } 11190 }); 11191 }); 11192 11193 return found; 11194 }; 11195 11196 var foundSourceAndTech = void 0; 11197 var flip = function flip(fn) { 11198 return function (a, b) { 11199 return fn(b, a); 11200 }; 11201 }; 11202 var finder = function finder(_ref2, source) { 11203 var techName = _ref2[0], 11204 tech = _ref2[1]; 11205 11206 if (tech.canPlaySource(source, _this4.options_[techName.toLowerCase()])) { 11207 return { source: source, tech: techName }; 11208 } 11209 }; 11210 11211 // Depending on the truthiness of `options.sourceOrder`, we swap the order of techs and sources 11212 // to select from them based on their priority. 11213 if (this.options_.sourceOrder) { 11214 // Source-first ordering 11215 foundSourceAndTech = findFirstPassingTechSourcePair(sources, techs, flip(finder)); 11216 } else { 11217 // Tech-first ordering 11218 foundSourceAndTech = findFirstPassingTechSourcePair(techs, sources, finder); 11219 } 11220 11221 return foundSourceAndTech || false; 11222 }; 11223 11224 /** 11225 * The source function updates the video source 11226 * There are three types of variables you can pass as the argument. 11227 * **URL string**: A URL to the the video file. Use this method if you are sure 11228 * the current playback technology (HTML5/Flash) can support the source you 11229 * provide. Currently only MP4 files can be used in both HTML5 and Flash. 11230 * 11231 * @param {Tech~SourceObject|Tech~SourceObject[]} [source] 11232 * One SourceObject or an array of SourceObjects 11233 * 11234 * @return {string|Player} 11235 * - The current video source when getting 11236 * - The player when setting 11237 */ 11238 11239 11240 Player.prototype.src = function src(source) { 11241 if (source === undefined) { 11242 return this.techGet_('src'); 11243 } 11244 11245 var currentTech = _tech2['default'].getTech(this.techName_); 11246
11247 // Support old behavior of techs being registered as components. 11248 // Remove once that deprecated behavior is removed. 11249 if (!currentTech) { 11250 currentTech = _component2['default'].getComponent(this.techName_); 11251 } 11252 11253 // case: Array of source objects to choose from and pick the best to play 11254 if (Array.isArray(source)) { 11255 this.sourceList_(source); 11256 11257 // case: URL String (http://myvideo...) 11258 } else if (typeof source === 'string') { 11259 // create a source object from the string 11260 this.src({ src: source }); 11261 11262 // case: Source object { src: '', type: '' ... } 11263 } else if (source instanceof Object) { 11264 // check if the source has a type and the loaded tech cannot play the source 11265 // if there's no type we'll just try the current tech 11266 if (source.type && !currentTech.canPlaySource(source, this.options_[this.techName_.toLowerCase()])) { 11267 // create a source list with the current source and send through 11268 // the tech loop to check for a compatible technology 11269 this.sourceList_([source]); 11270 } else { 11271 this.cache_.sources = null; 11272 this.cache_.source = source; 11273 this.cache_.src = source.src; 11274 11275 this.currentType_ = source.type || ''; 11276 11277 // wait until the tech is ready to set the source 11278 this.ready(function () { 11279 11280 // The setSource tech method was added with source handlers 11281 // so older techs won't support it 11282 // We need to check the direct prototype for the case where subclasses 11283 // of the tech do not support source handlers 11284 if (currentTech.prototype.hasOwnProperty('setSource')) { 11285 this.techCall_('setSource', source); 11286 } else { 11287 this.techCall_('src', source.src); 11288 } 11289 11290 if (this.options_.preload === 'auto') { 11291 this.load(); 11292 } 11293 11294 if (this.options_.autoplay) { 11295 this.play(); 11296 } 11297 11298 // Set the source synchronously if possible (#2326) 11299 }, true); 11300 } 11301 } 11302 11303 return this; 11304 }; 11305 11306 /** 11307 * Handle an array of source objects 11308 * 11309 * @param {Tech~SourceObject[]} sources 11310 * Array of source objects 11311 * 11312 * @private 11313 */ 11314 11315 11316 Player.prototype.sourceList_ = function sourceList_(sources) { 11317 var sourceTech = this.selectSource(sources); 11318 11319 if (sourceTech) { 11320 if (sourceTech.tech === this.techName_) { 11321 // if this technology is already loaded, set the source 11322 this.src(sourceTech.source); 11323 } else { 11324 // load this technology with the chosen source 11325 this.loadTech_(sourceTech.tech, sourceTech.source); 11326 } 11327 11328 this.cache_.sources = sources; 11329 } else { 11330 // We need to wrap this in a timeout to give folks a chance to add error event handlers 11331 this.setTimeout(function () { 11332 this.error({ code: 4, message: this.localize(this.options_.notSupportedMessage) }); 11333 }, 0); 11334 11335 // we could not find an appropriate tech, but let's still notify the delegate that this is it 11336 // this needs a better comment about why this is needed 11337 this.triggerReady(); 11338 } 11339 }; 11340 11341 /** 11342 * Begin loading the src data. 11343 * 11344 * @return {Player} 11345 * A reference to the player 11346 */ 11347 11348 11349 Player.prototype.load = function load() { 11350 this.techCall_('load'); 11351 return this; 11352 }; 11353 11354 /** 11355 * Reset the player. Loads the first tech in the techOrder, 11356 * and calls `reset` on the tech`. 11357 * 11358 * @return {Player} 11359 * A reference to the player 11360 */ 11361 11362 11363 Player.prototype.reset = function reset() { 11364 this.loadTech_((0, _toTitleCase2['default'])(this.options_.techOrder[0]), null); 11365 this.techCall_('reset'); 11366 return this; 11367 }; 11368 11369 /** 11370 * Returns all of the current source objects. 11371 * 11372 * @return {Tech~SourceObject[]} 11373 * The current source objects 11374 */ 11375 11376 11377 Player.prototype.currentSources = function currentSources() { 11378 var source = this.currentSource(); 11379 var sources = []; 11380 11381 // assume `{}` or `{ src }` 11382 if (Object.keys(source).length !== 0) { 11383 sources.push(source); 11384 } 11385 11386 return this.cache_.sources || sources; 11387 }; 11388 11389 /** 11390 * Returns the current source object. 11391 * 11392 * @return {Tech~SourceObject} 11393 * The current source object 11394 */ 11395 11396 11397 Player.prototype.currentSource = function currentSource() { 11398 var source = {}; 11399 var src = this.currentSrc(); 11400 11401 if (src) { 11402 source.src = src; 11403 } 11404 11405 return this.cache_.source || source; 11406 }; 11407 11408 /** 11409 * Returns the fully qualified URL of the current source value e.g. http://mysite.com/video.mp4 11410 * Can be used in conjuction with `currentType` to assist in rebuilding the current source object. 11411 * 11412 * @return {string} 11413 * The current source 11414 */ 11415 11416 11417 Player.prototype.currentSrc = function currentSrc() { 11418 return this.techGet_('currentSrc') || this.cache_.src || ''; 11419 }; 11420 11421 /** 11422 * Get the current source type e.g. video/mp4 11423 * This can allow you rebuild the current source object so that you could load the same 11424 * source and tech later 11425 * 11426 * @return {string} 11427 * The source MIME type 11428 */ 11429 11430 11431 Player.prototype.currentType = function currentType() { 11432 return this.currentType_ || ''; 11433 }; 11434 11435 /** 11436 * Get or set the preload attribute 11437 * 11438 * @param {boolean} [value] 11439 * - true means that we should preload 11440 * - false maens that we should not preload 11441 * 11442 * @return {string|Player} 11443 * - the preload attribute value when getting 11444 * - the player when setting 11445 */ 11446 11447 11448 Player.prototype.preload = function preload(value) { 11449 if (value !== undefined) { 11450 this.techCall_('setPreload', value); 11451 this.options_.preload = value; 11452 return this; 11453 } 11454 return this.techGet_('preload'); 11455 }; 11456 11457 /** 11458 * Get or set the autoplay attribute. 11459 * 11460 * @param {boolean} [value] 11461 * - true means that we should autoplay 11462 * - false maens that we should not autoplay 11463 * 11464 * @return {string|Player} 11465 * - the current value of autoplay 11466 * - the player when setting 11467 */ 11468 11469 11470 Player.prototype.autoplay = function autoplay(value) { 11471 if (value !== undefined) { 11472 this.techCall_('setAutoplay', value); 11473 this.options_.autoplay = value; 11474 return this; 11475 } 11476 return this.techGet_('autoplay', value); 11477 }; 11478 11479 /** 11480 * Set or unset the playsinline attribute. 11481 * Playsinline tells the browser that non-fullscreen playback is preferred. 11482 * 11483 * @param {boolean} [value] 11484 * - true means that we should try to play inline by default 11485 * - false means that we should use the browser's default playback mode, 11486 * which in most cases is inline. iOS Safari is a notable exception 11487 * and plays fullscreen by default. 11488 * 11489 * @return {string|Player} 11490 * - the current value of playsinline 11491 * - the player when setting 11492 * 11493 * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline} 11494 */ 11495 11496 11497 Player.prototype.playsinline = function playsinline(value) { 11498 if (value !== undefined) { 11499 this.techCall_('setPlaysinline', value); 11500 this.options_.playsinline = value; 11501 return this; 11502 } 11503 return this.techGet_('playsinline'); 11504 }; 11505 11506 /** 11507 * Get or set the loop attribute on the video element. 11508 * 11509 * @param {boolean} [value] 11510 * - true means that we should loop the video 11511 * - false means that we should not loop the video 11512 * 11513 * @return {string|Player} 11514 * - the current value of loop when getting 11515 * - the player when setting 11516 */ 11517 11518 11519 Player.prototype.loop = function loop(value) { 11520 if (value !== undefined) { 11521 this.techCall_('setLoop', value); 11522 this.options_.loop = value; 11523 return this; 11524 } 11525 return this.techGet_('loop'); 11526 }; 11527 11528 /** 11529 * Get or set the poster image source url 11530 * 11531 * @fires Player#posterchange 11532 * 11533 * @param {string} [src] 11534 * Poster image source URL 11535 * 11536 * @return {string|Player} 11537 * - the current value of poster when getting 11538 * - the player when setting 11539 */ 11540 11541 11542 Player.prototype.poster = function poster(src) { 11543 if (src === undefined) { 11544 return this.poster_; 11545 } 11546 11547 // The correct way to remove a poster is to set as an empty string 11548 // other falsey values will throw errors 11549 if (!src) { 11550 src = ''; 11551 } 11552 11553 // update the internal poster variable 11554 this.poster_ = src; 11555 11556 // update the tech's poster 11557 this.techCall_('setPoster', src); 11558 11559 // alert components that the poster has been set 11560 /** 11561 * This event fires when the poster image is changed on the player. 11562 * 11563 * @event Player#posterchange 11564 * @type {EventTarget~Event} 11565 */ 11566 this.trigger('posterchange'); 11567 11568 return this; 11569 }; 11570 11571 /** 11572 * Some techs (e.g. YouTube) can provide a poster source in an 11573 * asynchronous way. We want the poster component to use this 11574 * poster source so that it covers up the tech's controls. 11575 * (YouTube's play button). However we only want to use this 11576 * soruce if the player user hasn't set a poster through 11577 * the normal APIs. 11578 * 11579 * @fires Player#posterchange 11580 * @listens Tech#posterchange 11581 * @private 11582 */ 11583 11584 11585 Player.prototype.handleTechPosterChange_ = function handleTechPosterChange_() { 11586 if (!this.poster_ && this.tech_ && this.tech_.poster) { 11587 this.poster_ = this.tech_.poster() || ''; 11588 11589 // Let components know the poster has changed 11590 this.trigger('posterchange'); 11591 } 11592 }; 11593 11594 /** 11595 * Get or set whether or not the controls are showing. 11596 * 11597 * @fires Player#controlsenabled 11598 * 11599 * @param {boolean} [bool] 11600 * - true to turn controls on 11601 * - false to turn controls off 11602 * 11603 * @return {boolean|Player} 11604 * - the current value of controls when getting 11605 * - the player when setting 11606 */ 11607 11608 11609 Player.prototype.controls = function controls(bool) { 11610 if (bool !== undefined) { 11611 bool = !!bool; 11612 11613 // Don't trigger a change event unless it actually changed 11614 if (this.controls_ !== bool) { 11615 this.controls_ = bool; 11616 11617 if (this.usingNativeControls()) { 11618 this.techCall_('setControls', bool); 11619 } 11620 11621 if (bool) { 11622 this.removeClass('vjs-controls-disabled'); 11623 this.addClass('vjs-controls-enabled'); 11624 /** 11625 * @event Player#controlsenabled 11626 * @type {EventTarget~Event} 11627 */ 11628 this.trigger('controlsenabled'); 11629 11630 if (!this.usingNativeControls()) {
11631 this.addTechControlsListeners_(); 11632 } 11633 } else { 11634 this.removeClass('vjs-controls-enabled'); 11635 this.addClass('vjs-controls-disabled'); 11636 /** 11637 * @event Player#controlsdisabled 11638 * @type {EventTarget~Event} 11639 */ 11640 this.trigger('controlsdisabled'); 11641 11642 if (!this.usingNativeControls()) { 11643 this.removeTechControlsListeners_(); 11644 } 11645 } 11646 } 11647 return this; 11648 } 11649 return !!this.controls_; 11650 }; 11651 11652 /** 11653 * Toggle native controls on/off. Native controls are the controls built into 11654 * devices (e.g. default iPhone controls), Flash, or other techs 11655 * (e.g. Vimeo Controls) 11656 * **This should only be set by the current tech, because only the tech knows 11657 * if it can support native controls** 11658 * 11659 * @fires Player#usingnativecontrols 11660 * @fires Player#usingcustomcontrols 11661 * 11662 * @param {boolean} [bool] 11663 * - true to turn native controls on 11664 * - false to turn native controls off 11665 * 11666 * @return {boolean|Player} 11667 * - the current value of native controls when getting 11668 * - the player when setting 11669 */ 11670 11671 11672 Player.prototype.usingNativeControls = function usingNativeControls(bool) { 11673 if (bool !== undefined) { 11674 bool = !!bool; 11675 11676 // Don't trigger a change event unless it actually changed 11677 if (this.usingNativeControls_ !== bool) { 11678 this.usingNativeControls_ = bool; 11679 if (bool) { 11680 this.addClass('vjs-using-native-controls'); 11681 11682 /** 11683 * player is using the native device controls 11684 * 11685 * @event Player#usingnativecontrols 11686 * @type {EventTarget~Event} 11687 */ 11688 this.trigger('usingnativecontrols'); 11689 } else { 11690 this.removeClass('vjs-using-native-controls'); 11691 11692 /** 11693 * player is using the custom HTML controls 11694 * 11695 * @event Player#usingcustomcontrols 11696 * @type {EventTarget~Event} 11697 */ 11698 this.trigger('usingcustomcontrols'); 11699 } 11700 } 11701 return this; 11702 } 11703 return !!this.usingNativeControls_; 11704 }; 11705 11706 /** 11707 * Set or get the current MediaError 11708 * 11709 * @fires Player#error 11710 * 11711 * @param {MediaError|string|number} [err] 11712 * A MediaError or a string/number to be turned 11713 * into a MediaError 11714 * 11715 * @return {MediaError|null|Player} 11716 * - The current MediaError when getting (or null) 11717 * - The player when setting 11718 */ 11719 11720 11721 Player.prototype.error = function error(err) { 11722 if (err === undefined) { 11723 return this.error_ || null; 11724 } 11725 11726 // restoring to default 11727 if (err === null) { 11728 this.error_ = err; 11729 this.removeClass('vjs-error'); 11730 if (this.errorDisplay) { 11731 this.errorDisplay.close(); 11732 } 11733 return this; 11734 } 11735 11736 this.error_ = new _mediaError2['default'](err); 11737 11738 // add the vjs-error classname to the player 11739 this.addClass('vjs-error'); 11740 11741 // log the name of the error type and any message 11742 // ie8 just logs "[object object]" if you just log the error object 11743 _log2['default'].error('(CODE:' + this.error_.code + ' ' + _mediaError2['default'].errorTypes[this.error_.code] + ')', this.error_.message, this.error_); 11744 11745 /** 11746 * @event Player#error 11747 * @type {EventTarget~Event} 11748 */ 11749 this.trigger('error'); 11750 11751 return this; 11752 }; 11753 11754 /** 11755 * Report user activity 11756 * 11757 * @param {Object} event 11758 * Event object 11759 */ 11760 11761 11762 Player.prototype.reportUserActivity = function reportUserActivity(event) { 11763 this.userActivity_ = true; 11764 }; 11765 11766 /** 11767 * Get/set if user is active 11768 * 11769 * @fires Player#useractive 11770 * @fires Player#userinactive 11771 * 11772 * @param {boolean} [bool] 11773 * - true if the user is active 11774 * - false if the user is inactive 11775 * @return {boolean|Player} 11776 * - the current value of userActive when getting 11777 * - the player when setting 11778 */ 11779 11780 11781 Player.prototype.userActive = function userActive(bool) { 11782 if (bool !== undefined) { 11783 bool = !!bool; 11784 if (bool !== this.userActive_) { 11785 this.userActive_ = bool; 11786 if (bool) { 11787 // If the user was inactive and is now active we want to reset the 11788 // inactivity timer 11789 this.userActivity_ = true; 11790 this.removeClass('vjs-user-inactive'); 11791 this.addClass('vjs-user-active'); 11792 /** 11793 * @event Player#useractive 11794 * @type {EventTarget~Event} 11795 */ 11796 this.trigger('useractive'); 11797 } else { 11798 // We're switching the state to inactive manually, so erase any other 11799 // activity 11800 this.userActivity_ = false;
vendor: 3,493 bytes, lines 11801-11893
11801 11802 // Chrome/Safari/IE have bugs where when you change the cursor it can 11803 // trigger a mousemove event. This causes an issue when you're hiding 11804 // the cursor when the user is inactive, and a mousemove signals user 11805 // activity. Making it impossible to go into inactive mode. Specifically 11806 // this happens in fullscreen when we really need to hide the cursor. 11807 // 11808 // When this gets resolved in ALL browsers it can be removed 11809 // https://code.google.com/p/chromium/issues/detail?id=103041 11810 if (this.tech_) { 11811 this.tech_.one('mousemove', function (e) { 11812 e.stopPropagation(); 11813 e.preventDefault(); 11814 }); 11815 } 11816 11817 this.removeClass('vjs-user-active'); 11818 this.addClass('vjs-user-inactive'); 11819 /** 11820 * @event Player#userinactive 11821 * @type {EventTarget~Event} 11822 */ 11823 this.trigger('userinactive'); 11824 } 11825 } 11826 return this; 11827 } 11828 return this.userActive_; 11829 }; 11830 11831 /** 11832 * Listen for user activity based on timeout value 11833 * 11834 * @private 11835 */ 11836 11837 11838 Player.prototype.listenForUserActivity_ = function listenForUserActivity_() { 11839 var mouseInProgress = void 0; 11840 var lastMoveX = void 0; 11841 var lastMoveY = void 0; 11842 var handleActivity = Fn.bind(this, this.reportUserActivity); 11843 11844 var handleMouseMove = function handleMouseMove(e) { 11845 // #1068 - Prevent mousemove spamming 11846 // Chrome Bug: https://code.google.com/p/chromium/issues/detail?id=366970 11847 if (e.screenX !== lastMoveX || e.screenY !== lastMoveY) { 11848 lastMoveX = e.screenX; 11849 lastMoveY = e.screenY; 11850 handleActivity(); 11851 } 11852 }; 11853 11854 var handleMouseDown = function handleMouseDown() { 11855 handleActivity(); 11856 // For as long as the they are touching the device or have their mouse down, 11857 // we consider them active even if they're not moving their finger or mouse. 11858 // So we want to continue to update that they are active 11859 this.clearInterval(mouseInProgress); 11860 // Setting userActivity=true now and setting the interval to the same time 11861 // as the activityCheck interval (250) should ensure we never miss the 11862 // next activityCheck 11863 mouseInProgress = this.setInterval(handleActivity, 250); 11864 }; 11865 11866 var handleMouseUp = function handleMouseUp(event) { 11867 handleActivity(); 11868 // Stop the interval that maintains activity if the mouse/touch is down 11869 this.clearInterval(mouseInProgress); 11870 }; 11871 11872 // Any mouse movement will be considered user activity 11873 this.on('mousedown', handleMouseDown); 11874 this.on('mousemove', handleMouseMove); 11875 this.on('mouseup', handleMouseUp); 11876 11877 // Listen for keyboard navigation 11878 // Shouldn't need to use inProgress interval because of key repeat 11879 this.on('keydown', handleActivity); 11880 this.on('keyup', handleActivity); 11881 11882 // Run an interval every 250 milliseconds instead of stuffing everything into 11883 // the mousemove/touchmove function itself, to prevent performance degradation. 11884 // `this.reportUserActivity` simply sets this.userActivity_ to true, which 11885 // then gets picked up by this loop 11886 // http://ejohn.org/blog/learning-from-twitter/ 11887 var inactivityTimeout = void 0; 11888 11889 this.setInterval(function () { 11890 // Check to see if mouse/touch activity has happened 11891 if (this.userActivity_) { 11892 // Reset the activity tracker 11893 this.userActivity_ = false;
11894 11895 // If the user state was inactive, set the state to active 11896 this.userActive(true); 11897 11898 // Clear any existing inactivity timeout to start the timer over 11899 this.clearTimeout(inactivityTimeout); 11900 11901 var timeout = this.options_.inactivityTimeout; 11902 11903 if (timeout > 0) { 11904 // In <timeout> milliseconds, if no more activity has occurred the 11905 // user will be considered inactive 11906 inactivityTimeout = this.setTimeout(function () { 11907 // Protect against the case where the inactivityTimeout can trigger just 11908 // before the next user activity is picked up by the activity check loop 11909 // causing a flicker 11910 if (!this.userActivity_) { 11911 this.userActive(false); 11912 } 11913 }, timeout); 11914 } 11915 } 11916 }, 250); 11917 }; 11918 11919 /** 11920 * Gets or sets the current playback rate. A playback rate of 11921 * 1.0 represents normal speed and 0.5 would indicate half-speed 11922 * playback, for instance. 11923 * 11924 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-playbackrate 11925 * 11926 * @param {number} [rate] 11927 * New playback rate to set. 11928 * 11929 * @return {number|Player} 11930 * - The current playback rate when getting or 1.0 11931 * - the player when setting 11932 */ 11933 11934 11935 Player.prototype.playbackRate = function playbackRate(rate) { 11936 if (rate !== undefined) { 11937 this.techCall_('setPlaybackRate', rate); 11938 return this; 11939 } 11940 11941 if (this.tech_ && this.tech_.featuresPlaybackRate) { 11942 return this.techGet_('playbackRate'); 11943 } 11944 return 1.0; 11945 }; 11946 11947 /** 11948 * Gets or sets the audio flag 11949 * 11950 * @param {boolean} bool 11951 * - true signals that this is an audio player 11952 * - false signals that this is not an audio player 11953 * 11954 * @return {Player|boolean} 11955 * - the current value of isAudio when getting 11956 * - the player if setting 11957 */ 11958 11959 11960 Player.prototype.isAudio = function isAudio(bool) { 11961 if (bool !== undefined) { 11962 this.isAudio_ = !!bool; 11963 return this; 11964 } 11965 11966 return !!this.isAudio_; 11967 }; 11968 11969 /** 11970 * Get the {@link VideoTrackList} 11971 * 11972 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist 11973 * 11974 * @return {VideoTrackList} 11975 * the current video track list 11976 */ 11977 11978 11979 Player.prototype.videoTracks = function videoTracks() { 11980 // if we have not yet loadTech_, we create videoTracks_ 11981 // these will be passed to the tech during loading 11982 if (!this.tech_) { 11983 this.videoTracks_ = this.videoTracks_ || new _videoTrackList2['default'](); 11984 return this.videoTracks_; 11985 } 11986 11987 return this.tech_.videoTracks(); 11988 }; 11989 11990 /** 11991 * Get the {@link AudioTrackList} 11992 * 11993 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist 11994 * 11995 * @return {AudioTrackList} 11996 * the current audio track list 11997 */ 11998 11999 12000 Player.prototype.audioTracks = function audioTracks() { 12001 // if we have not yet loadTech_, we create videoTracks_ 12002 // these will be passed to the tech during loading 12003 if (!this.tech_) { 12004 this.audioTracks_ = this.audioTracks_ || new _audioTrackList2['default'](); 12005 return this.audioTracks_; 12006 } 12007 12008 return this.tech_.audioTracks(); 12009 }; 12010 12011 /** 12012 * Get the {@link TextTrackList} 12013 * 12014 * Text tracks are tracks of timed text events. 12015 * - Captions: text displayed over the video 12016 * for the hearing impaired 12017 * - Subtitles: text displayed over the video for 12018 * those who don't understand language in the video 12019 * - Chapters: text displayed in a menu allowing the user to jump 12020 * to particular points (chapters) in the video 12021 * - Descriptions: (not yet implemented) audio descriptions that are read back to 12022 * the user by a screen reading device 12023 * 12024 * @see http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-texttracks 12025 * 12026 * @return {TextTrackList|undefined} 12027 * The current TextTrackList or undefined if 12028 * or undefined if we don't have a tech 12029 */ 12030 12031 12032 Player.prototype.textTracks = function textTracks() { 12033 // cannot use techGet_ directly because it checks to see whether the tech is ready. 12034 // Flash is unlikely to be ready in time but textTracks should still work. 12035 if (this.tech_) { 12036 return this.tech_.textTracks(); 12037 } 12038 }; 12039 12040 /** 12041 * Get the "remote" {@link TextTrackList}. Remote Text Tracks 12042 * are tracks that were added to the HTML video element and can 12043 * be removed, whereas normal texttracks cannot be removed. 12044 * 12045 * 12046 * @return {TextTrackList|undefined} 12047 * The current remote text track list or undefined 12048 * if we don't have a tech 12049 */ 12050 12051 12052 Player.prototype.remoteTextTracks = function remoteTextTracks() { 12053 if (this.tech_) { 12054 return this.tech_.remoteTextTracks(); 12055 } 12056 }; 12057 12058 /** 12059 * Get the "remote" {@link HTMLTrackElementList}. 12060 * This gives the user all of the DOM elements that match up 12061 * with the remote {@link TextTrackList}. 12062 * 12063 * @return {HTMLTrackElementList} 12064 * The current remote text track list elements 12065 * or undefined if we don't have a tech 12066 */ 12067 12068 12069 Player.prototype.remoteTextTrackEls = function remoteTextTrackEls() { 12070 if (this.tech_) { 12071 return this.tech_.remoteTextTrackEls(); 12072 } 12073 }; 12074 12075 /** 12076 * A helper method for adding a {@link TextTrack} to our 12077 * {@link TextTrackList}. 12078 * 12079 * In addition to the W3C settings we allow adding additional info through options. 12080 * 12081 * @see http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-addtexttrack 12082 * 12083 * @param {string} [kind] 12084 * the kind of TextTrack you are adding 12085 * 12086 * @param {string} [label] 12087 * the label to give the TextTrack label 12088 * 12089 * @param {string} [language] 12090 * the language to set on the TextTrack 12091 * 12092 * @return {TextTrack|undefined} 12093 * the TextTrack that was added or undefined 12094 * if there is no tech 12095 */ 12096 12097 12098 Player.prototype.addTextTrack = function addTextTrack(kind, label, language) { 12099 if (this.tech_) { 12100 return this.tech_.addTextTrack(kind, label, language); 12101 } 12102 }; 12103 12104 /** 12105 * Create a remote {@link TextTrack} and an {@link HTMLTrackElement}. It will 12106 * automatically removed from the video element whenever the source changes, unless 12107 * manualCleanup is set to false. 12108 * 12109 * @param {Object} options 12110 * Options to pass to {@link HTMLTrackElement} during creation. See 12111 * {@link HTMLTrackElement} for object properties that you should use. 12112 * 12113 * @param {boolean} [manualCleanup=true] if set to false, the TextTrack will be 12114 * 12115 * @return {HTMLTrackElement} 12116 * the HTMLTrackElement that was created and added 12117 * to the HTMLTrackElementList and the remote 12118 * TextTrackList 12119 * 12120 * @deprecated The default value of the "manualCleanup" parameter will default
12121 * to "false" in upcoming versions of Video.js 12122 */ 12123 12124 12125 Player.prototype.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) { 12126 if (this.tech_) { 12127 return this.tech_.addRemoteTextTrack(options, manualCleanup); 12128 } 12129 }; 12130 12131 /** 12132 * Remove a remote {@link TextTrack} from the respective 12133 * {@link TextTrackList} and {@link HTMLTrackElementList}. 12134 * 12135 * @param {Object} track 12136 * Remote {@link TextTrack} to remove 12137 * 12138 * @return {undefined} 12139 * does not return anything 12140 */ 12141 12142 12143 Player.prototype.removeRemoteTextTrack = function removeRemoteTextTrack() { 12144 var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, 12145 _ref3$track = _ref3.track, 12146 track = _ref3$track === undefined ? arguments[0] : _ref3$track; 12147 12148 // destructure the input into an object with a track argument, defaulting to arguments[0] 12149 // default the whole argument to an empty object if nothing was passed in 12150 12151 if (this.tech_) { 12152 return this.tech_.removeRemoteTextTrack(track); 12153 } 12154 }; 12155 12156 /** 12157 * Gets available media playback quality metrics as specified by the W3C's Media 12158 * Playback Quality API. 12159 * 12160 * @see [Spec]{@link https://wicg.github.io/media-playback-quality} 12161 * 12162 * @return {Object|undefined} 12163 * An object with supported media playback quality metrics or undefined if there 12164 * is no tech or the tech does not support it. 12165 */ 12166 12167 12168 Player.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() { 12169 return this.techGet_('getVideoPlaybackQuality'); 12170 }; 12171 12172 /** 12173 * Get video width 12174 * 12175 * @return {number} 12176 * current video width 12177 */ 12178 12179 12180 Player.prototype.videoWidth = function videoWidth() { 12181 return this.tech_ && this.tech_.videoWidth && this.tech_.videoWidth() || 0; 12182 }; 12183 12184 /** 12185 * Get video height 12186 * 12187 * @return {number} 12188 * current video height 12189 */ 12190 12191 12192 Player.prototype.videoHeight = function videoHeight() { 12193 return this.tech_ && this.tech_.videoHeight && this.tech_.videoHeight() || 0; 12194 }; 12195 12196 // Methods to add support for 12197 // initialTime: function() { return this.techCall_('initialTime'); }, 12198 // startOffsetTime: function() { return this.techCall_('startOffsetTime'); }, 12199 // played: function() { return this.techCall_('played'); }, 12200 // defaultPlaybackRate: function() { return this.techCall_('defaultPlaybackRate'); }, 12201 // defaultMuted: function() { return this.techCall_('defaultMuted'); } 12202 12203 /** 12204 * The player's language code 12205 * NOTE: The language should be set in the player options if you want the 12206 * the controls to be built with a specific language. Changing the lanugage 12207 * later will not update controls text. 12208 * 12209 * @param {string} [code] 12210 * the language code to set the player to 12211 * 12212 * @return {string|Player} 12213 * - The current language code when getting 12214 * - A reference to the player when setting 12215 */ 12216 12217 12218 Player.prototype.language = function language(code) { 12219 if (code === undefined) { 12220 return this.language_; 12221 } 12222 12223 this.language_ = String(code).toLowerCase(); 12224 return this; 12225 }; 12226 12227 /** 12228 * Get the player's language dictionary 12229 * Merge every time, because a newly added plugin might call videojs.addLanguage() at any time 12230 * Languages specified directly in the player options have precedence 12231 * 12232 * @return {Array} 12233 * An array of of supported languages 12234 */ 12235 12236 12237 Player.prototype.languages = function languages() { 12238 return (0, _mergeOptions2['default'])(Player.prototype.options_.languages, this.languages_); 12239 }; 12240 12241 /** 12242 * returns a JavaScript object reperesenting the current track 12243 * information. **DOES not return it as JSON** 12244 * 12245 * @return {Object} 12246 * Object representing the current of track info 12247 */ 12248 12249 12250 Player.prototype.toJSON = function toJSON() { 12251 var options = (0, _mergeOptions2['default'])(this.options_); 12252 var tracks = options.tracks; 12253 12254 options.tracks = []; 12255 12256 for (var i = 0; i < tracks.length; i++) { 12257 var track = tracks[i]; 12258 12259 // deep merge tracks and null out player so no circular references 12260 track = (0, _mergeOptions2['default'])(track); 12261 track.player = undefined; 12262 options.tracks[i] = track; 12263 } 12264 12265 return options; 12266 }; 12267 12268 /** 12269 * Creates a simple modal dialog (an instance of the {@link ModalDialog} 12270 * component) that immediately overlays the player with arbitrary 12271 * content and removes itself when closed. 12272 * 12273 * @param {string|Function|Element|Array|null} content 12274 * Same as {@link ModalDialog#content}'s param of the same name. 12275 * The most straight-forward usage is to provide a string or DOM 12276 * element. 12277 * 12278 * @param {Object} [options] 12279 * Extra options which will be passed on to the {@link ModalDialog}. 12280 * 12281 * @return {ModalDialog} 12282 * the {@link ModalDialog} that was created 12283 */ 12284 12285 12286 Player.prototype.createModal = function createModal(content, options) { 12287 var _this5 = this; 12288 12289 options = options || {}; 12290 options.content = content || ''; 12291 12292 var modal = new _modalDialog2['default'](this, options); 12293 12294 this.addChild(modal); 12295 modal.on('dispose', function () { 12296 _this5.removeChild(modal); 12297 }); 12298 12299 return modal.open(); 12300 }; 12301 12302 /** 12303 * Gets tag settings 12304 * 12305 * @param {Element} tag 12306 * The player tag 12307 * 12308 * @return {Object}
12309 * An object containing all of the settings 12310 * for a player tag 12311 */ 12312 12313 12314 Player.getTagSettings = function getTagSettings(tag) { 12315 var baseOptions = { 12316 sources: [], 12317 tracks: [] 12318 }; 12319 12320 var tagOptions = Dom.getElAttributes(tag); 12321 var dataSetup = tagOptions['data-setup']; 12322 12323 if (Dom.hasElClass(tag, 'vjs-fluid')) { 12324 tagOptions.fluid = true; 12325 } 12326 12327 // Check if data-setup attr exists. 12328 if (dataSetup !== null) { 12329 // Parse options JSON 12330 // If empty string, make it a parsable json object. 12331 var _safeParseTuple = (0, _tuple2['default'])(dataSetup || '{}'), 12332 err = _safeParseTuple[0], 12333 data = _safeParseTuple[1]; 12334 12335 if (err) { 12336 _log2['default'].error(err); 12337 } 12338 (0, _obj.assign)(tagOptions, data); 12339 } 12340 12341 (0, _obj.assign)(baseOptions, tagOptions); 12342 12343 // Get tag children settings 12344 if (tag.hasChildNodes()) { 12345 var children = tag.childNodes; 12346 12347 for (var i = 0, j = children.length; i < j; i++) { 12348 var child = children[i]; 12349 // Change case needed: http://ejohn.org/blog/nodename-case-sensitivity/ 12350 var childName = child.nodeName.toLowerCase(); 12351 12352 if (childName === 'source') { 12353 baseOptions.sources.push(Dom.getElAttributes(child)); 12354 } else if (childName === 'track') { 12355 baseOptions.tracks.push(Dom.getElAttributes(child)); 12356 } 12357 } 12358 } 12359 12360 return baseOptions; 12361 }; 12362 12363 /** 12364 * Determine wether or not flexbox is supported 12365 * 12366 * @return {boolean} 12367 * - true if flexbox is supported 12368 * - false if flexbox is not supported 12369 */ 12370 12371 12372 Player.prototype.flexNotSupported_ = function flexNotSupported_() { 12373 var elem = _document2['default'].createElement('i'); 12374 12375 // Note: We don't actually use flexBasis (or flexOrder), but it's one of the more 12376 // common flex features that we can rely on when checking for flex support. 12377 return !('flexBasis' in elem.style || 'webkitFlexBasis' in elem.style || 'mozFlexBasis' in elem.style || 'msFlexBasis' in elem.style || 12378 // IE10-specific (2012 flex spec) 12379 'msFlexOrder' in elem.style); 12380 }; 12381 12382 return Player; 12383}(_component2['default']); 12384 12385/** 12386 * Global player list 12387 * 12388 * @type {Object} 12389 */ 12390 12391 12392Player.players = {}; 12393 12394var navigator = _window2['default'].navigator; 12395 12396/* 12397 * Player instance options, surfaced using options 12398 * options = Player.prototype.options_ 12399 * Make changes in options, not here. 12400 * 12401 * @type {Object} 12402 * @private 12403 */ 12404Player.prototype.options_ = { 12405 // Default order of fallback technology 12406 techOrder: ['html5', 'flash'], 12407 // techOrder: ['flash','html5'], 12408 12409 html5: {}, 12410 flash: {}, 12411 12412 // defaultVolume: 0.85, 12413 defaultVolume: 0.00, 12414 12415 // default inactivity timeout 12416 inactivityTimeout: 2000, 12417 12418 // default playback rates 12419 playbackRates: [], 12420 // Add playback rate selection by adding rates 12421 // 'playbackRates': [0.5, 1, 1.5, 2], 12422 12423 // Included control sets 12424 children: ['mediaLoader', 'posterImage', 'textTrackDisplay', 'loadingSpinner', 'bigPlayButton', 'controlBar', 'errorDisplay', 'textTrackSettings'], 12425 12426 language: navigator && (navigator.languages && navigator.languages[0] || navigator.userLanguage || navigator.language) || 'en', 12427 12428 // locales and their language translations 12429 languages: {}, 12430 12431 // Default message to show when a video cannot be played. 12432 notSupportedMessage: 'No compatible source was found for this media.' 12433}; 12434 12435[ 12436/** 12437 * Returns whether or not the player is in the "ended" state. 12438 * 12439 * @return {Boolean} True if the player is in the ended state, false if not. 12440 * @method Player#ended 12441 */ 12442'ended', 12443/** 12444 * Returns whether or not the player is in the "seeking" state. 12445 * 12446 * @return {Boolean} True if the player is in the seeking state, false if not. 12447 * @method Player#seeking 12448 */ 12449'seeking', 12450/** 12451 * Returns the TimeRanges of the media that are currently available 12452 * for seeking to. 12453 * 12454 * @return {TimeRanges} the seekable intervals of the media timeline 12455 * @method Player#seekable 12456 */ 12457'seekable', 12458/** 12459 * Returns the current state of network activity for the element, from 12460 * the codes in the list below. 12461 * - NETWORK_EMPTY (numeric value 0) 12462 * The element has not yet been initialised. All attributes are in 12463 * their initial states. 12464 * - NETWORK_IDLE (numeric value 1) 12465 * The element's resource selection algorithm is active and has 12466 * selected a resource, but it is not actually using the network at 12467 * this time. 12468 * - NETWORK_LOADING (numeric value 2) 12469 * The user agent is actively trying to download data. 12470 * - NETWORK_NO_SOURCE (numeric value 3) 12471 * The element's resource selection algorithm is active, but it has 12472 * not yet found a resource to use. 12473 * 12474 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#network-states 12475 * @return {number} the current network activity state 12476 * @method Player#networkState 12477 */ 12478'networkState', 12479/** 12480 * Returns a value that expresses the current state of the element 12481 * with respect to rendering the current playback position, from the 12482 * codes in the list below.
12483 * - HAVE_NOTHING (numeric value 0) 12484 * No information regarding the media resource is available. 12485 * - HAVE_METADATA (numeric value 1) 12486 * Enough of the resource has been obtained that the duration of the 12487 * resource is available. 12488 * - HAVE_CURRENT_DATA (numeric value 2) 12489 * Data for the immediate current playback position is available. 12490 * - HAVE_FUTURE_DATA (numeric value 3) 12491 * Data for the immediate current playback position is available, as 12492 * well as enough data for the user agent to advance the current 12493 * playback position in the direction of playback. 12494 * - HAVE_ENOUGH_DATA (numeric value 4) 12495 * The user agent estimates that enough data is available for 12496 * playback to proceed uninterrupted. 12497 * 12498 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-readystate 12499 * @return {number} the current playback rendering state 12500 * @method Player#readyState 12501 */ 12502'readyState'].forEach(function (fn) { 12503 Player.prototype[fn] = function () { 12504 return this.techGet_(fn); 12505 }; 12506}); 12507 12508TECH_EVENTS_RETRIGGER.forEach(function (event) { 12509 Player.prototype['handleTech' + (0, _toTitleCase2['default'])(event) + '_'] = function () { 12510 return this.trigger(event); 12511 }; 12512}); 12513 12514/** 12515 * Fired when the player has initial duration and dimension information 12516 * 12517 * @event Player#loadedmetadata 12518 * @type {EventTarget~Event} 12519 */ 12520 12521/** 12522 * Fired when the player has downloaded data at the current playback position 12523 * 12524 * @event Player#loadeddata 12525 * @type {EventTarget~Event} 12526 */ 12527 12528/** 12529 * Fired when the current playback position has changed * 12530 * During playback this is fired every 15-250 milliseconds, depending on the 12531 * playback technology in use. 12532 * 12533 * @event Player#timeupdate 12534 * @type {EventTarget~Event} 12535 */ 12536 12537/** 12538 * Fired when the volume changes 12539 * 12540 * @event Player#volumechange 12541 * @type {EventTarget~Event} 12542 */ 12543 12544_component2['default'].registerComponent('Player', Player); 12545exports['default'] = Player; 12546 12547},{"1":1,"4":4,"41":41,"44":44,"45":45,"46":46,"5":5,"50":50,"55":55,"59":59,"60":60,"61":61,"62":62,"63":63,"68":68,"69":69,"71":71,"76":76,"78":78,"79":79,"8":8,"81":81,"82":82,"83":83,"85":85,"86":86,"87":87,"88":88,"89":89,"90":90,"91":91,"94":94,"95":95,"97":97}],52:[function(_dereq_,module,exports){ 12548'use strict'; 12549 12550exports.__esModule = true; 12551 12552var _player = _dereq_(51); 12553 12554var _player2 = _interopRequireDefault(_player); 12555 12556function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 12557 12558/** 12559 * The method for registering a video.js plugin. {@link videojs:videojs.registerPlugin]. 12560 * 12561 * @param {string} name 12562 * The name of the plugin that is being registered 12563 * 12564 * @param {plugins:PluginFn} init 12565 * The function that gets run when a `Player` initializes. 12566 */ 12567var plugin = function plugin(name, init) { 12568 _player2['default'].prototype[name] = init; 12569}; /** 12570 * @file plugins.js 12571 * @module plugins 12572 */ 12573exports['default'] = plugin; 12574 12575},{"51":51}],53:[function(_dereq_,module,exports){ 12576'use strict'; 12577 12578exports.__esModule = true; 12579 12580var _clickableComponent = _dereq_(3); 12581 12582var _clickableComponent2 = _interopRequireDefault(_clickableComponent); 12583 12584var _component = _dereq_(5); 12585 12586var _component2 = _interopRequireDefault(_component); 12587 12588function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 12589 12590function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 12591 12592function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 12593 12594function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 12595 * @file popup-button.js 12596 */ 12597 12598 12599/** 12600 * A button class for use with {@link Popup} controls 12601 * 12602 * @extends ClickableComponent 12603 */ 12604var PopupButton = function (_ClickableComponent) { 12605 _inherits(PopupButton, _ClickableComponent); 12606 12607 /** 12608 * Create an instance of this class. 12609 * 12610 * @param {Player} player 12611 * The `Player` that this class should be attached to. 12612 * 12613 * @param {Object} [options] 12614 * The key/value store of player options. 12615 */ 12616 function PopupButton(player) { 12617 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 12618 12619 _classCallCheck(this, PopupButton); 12620 12621 var _this = _possibleConstructorReturn(this, _ClickableComponent.call(this, player, options)); 12622 12623 _this.update(); 12624 return _this; 12625 } 12626 12627 /** 12628 * Update the `Popup` that this button is attached to. 12629 */ 12630 12631
12632 PopupButton.prototype.update = function update() { 12633 var popup = this.createPopup(); 12634 12635 if (this.popup) { 12636 this.removeChild(this.popup); 12637 } 12638 12639 this.popup = popup; 12640 this.addChild(popup); 12641 12642 if (this.items && this.items.length === 0) { 12643 this.hide(); 12644 } else if (this.items && this.items.length > 1) { 12645 this.show(); 12646 } 12647 }; 12648 12649 /** 12650 * Create a `Popup`. - Override with specific functionality for component 12651 * 12652 * @abstract 12653 */ 12654 12655 12656 PopupButton.prototype.createPopup = function createPopup() {}; 12657 12658 /** 12659 * Create the `PopupButton`s DOM element. 12660 * 12661 * @return {Element} 12662 * The element that gets created. 12663 */ 12664 12665 12666 PopupButton.prototype.createEl = function createEl() { 12667 return _ClickableComponent.prototype.createEl.call(this, 'div', { 12668 className: this.buildCSSClass() 12669 }); 12670 }; 12671 12672 /** 12673 * Builds the default DOM `className`. 12674 * 12675 * @return {string} 12676 * The DOM `className` for this object. 12677 */ 12678 12679 12680 PopupButton.prototype.buildCSSClass = function buildCSSClass() { 12681 var menuButtonClass = 'vjs-menu-button'; 12682 12683 // If the inline option is passed, we want to use different styles altogether. 12684 if (this.options_.inline === true) { 12685 menuButtonClass += '-inline'; 12686 } else { 12687 menuButtonClass += '-popup'; 12688 } 12689 12690 return 'vjs-menu-button ' + menuButtonClass + ' ' + _ClickableComponent.prototype.buildCSSClass.call(this); 12691 }; 12692 12693 return PopupButton; 12694}(_clickableComponent2['default']); 12695 12696_component2['default'].registerComponent('PopupButton', PopupButton); 12697exports['default'] = PopupButton; 12698 12699},{"3":3,"5":5}],54:[function(_dereq_,module,exports){ 12700'use strict'; 12701 12702exports.__esModule = true; 12703 12704var _component = _dereq_(5); 12705 12706var _component2 = _interopRequireDefault(_component); 12707 12708var _dom = _dereq_(81); 12709 12710var Dom = _interopRequireWildcard(_dom); 12711 12712var _fn = _dereq_(83); 12713 12714var Fn = _interopRequireWildcard(_fn); 12715 12716var _events = _dereq_(82); 12717 12718var Events = _interopRequireWildcard(_events); 12719 12720function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 12721 12722function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 12723 12724function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 12725 12726function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 12727 12728function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 12729 * @file popup.js 12730 */ 12731 12732 12733/** 12734 * The Popup component is used to build pop up controls. 12735 * 12736 * @extends Component 12737 */ 12738var Popup = function (_Component) { 12739 _inherits(Popup, _Component); 12740 12741 function Popup() { 12742 _classCallCheck(this, Popup); 12743 12744 return _possibleConstructorReturn(this, _Component.apply(this, arguments)); 12745 } 12746 12747 /**
12748 * Add a popup item to the popup 12749 * 12750 * @param {Object|string} component 12751 * Component or component type to add 12752 * 12753 */ 12754 Popup.prototype.addItem = function addItem(component) { 12755 this.addChild(component); 12756 component.on('click', Fn.bind(this, function () { 12757 this.unlockShowing(); 12758 })); 12759 }; 12760 12761 /** 12762 * Create the `PopupButton`s DOM element. 12763 * 12764 * @return {Element} 12765 * The element that gets created. 12766 */ 12767 12768 12769 Popup.prototype.createEl = function createEl() { 12770 var contentElType = this.options_.contentElType || 'ul'; 12771 12772 this.contentEl_ = Dom.createEl(contentElType, { 12773 className: 'vjs-menu-content' 12774 }); 12775 12776 var el = _Component.prototype.createEl.call(this, 'div', { 12777 append: this.contentEl_, 12778 className: 'vjs-menu' 12779 }); 12780 12781 el.appendChild(this.contentEl_); 12782 12783 // Prevent clicks from bubbling up. Needed for Popup Buttons, 12784 // where a click on the parent is significant 12785 Events.on(el, 'click', function (event) { 12786 event.preventDefault(); 12787 event.stopImmediatePropagation(); 12788 }); 12789 12790 return el; 12791 }; 12792 12793 return Popup; 12794}(_component2['default']); 12795 12796_component2['default'].registerComponent('Popup', Popup); 12797exports['default'] = Popup; 12798 12799},{"5":5,"81":81,"82":82,"83":83}],55:[function(_dereq_,module,exports){ 12800'use strict'; 12801 12802exports.__esModule = true; 12803 12804var _clickableComponent = _dereq_(3); 12805 12806var _clickableComponent2 = _interopRequireDefault(_clickableComponent); 12807 12808var _component = _dereq_(5); 12809 12810var _component2 = _interopRequireDefault(_component); 12811 12812var _fn = _dereq_(83); 12813 12814var Fn = _interopRequireWildcard(_fn); 12815 12816var _dom = _dereq_(81); 12817 12818var Dom = _interopRequireWildcard(_dom); 12819 12820var _browser = _dereq_(78); 12821 12822var browser = _interopRequireWildcard(_browser); 12823 12824function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 12825 12826function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 12827 12828function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 12829 12830function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 12831 12832function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 12833 * @file poster-image.js 12834 */ 12835 12836 12837/** 12838 * A `ClickableComponent` that handles showing the poster image for the player. 12839 * 12840 * @extends ClickableComponent 12841 */ 12842var PosterImage = function (_ClickableComponent) { 12843 _inherits(PosterImage, _ClickableComponent); 12844 12845 /** 12846 * Create an instance of this class. 12847 * 12848 * @param {Player} player 12849 * The `Player` that this class should attach to. 12850 * 12851 * @param {Object} [options] 12852 * The key/value store of player options. 12853 */ 12854 function PosterImage(player, options) { 12855 _classCallCheck(this, PosterImage); 12856 12857 var _this = _possibleConstructorReturn(this, _ClickableComponent.call(this, player, options)); 12858 12859 _this.update(); 12860 player.on('posterchange', Fn.bind(_this, _this.update)); 12861 return _this; 12862 } 12863 12864 /** 12865 * Clean up and dispose of the `PosterImage`. 12866 */ 12867 12868 12869 PosterImage.prototype.dispose = function dispose() { 12870 this.player().off('posterchange', this.update); 12871 _ClickableComponent.prototype.dispose.call(this); 12872 }; 12873 12874 /** 12875 * Create the `PosterImage`s DOM element. 12876 * 12877 * @return {Element} 12878 * The element that gets created. 12879 */ 12880 12881 12882 PosterImage.prototype.createEl = function createEl() { 12883 var el = Dom.createEl('div', { 12884 className: 'vjs-poster', 12885 12886 // Don't want poster to be tabbable. 12887 tabIndex: -1 12888 }); 12889 12890 // To ensure the poster image resizes while maintaining its original aspect 12891 // ratio, use a div with `background-size` when available. For browsers that 12892 // do not support `background-size` (e.g. IE8), fall back on using a regular
12893 // img element. 12894 if (!browser.BACKGROUND_SIZE_SUPPORTED) { 12895 this.fallbackImg_ = Dom.createEl('img'); 12896 el.appendChild(this.fallbackImg_); 12897 } 12898 12899 return el; 12900 }; 12901 12902 /** 12903 * An {@link EventTarget~EventListener} for {@link Player#posterchange} events. 12904 * 12905 * @listens Player#posterchange 12906 * 12907 * @param {EventTarget~Event} [event] 12908 * The `Player#posterchange` event that triggered this function. 12909 */ 12910 12911 12912 PosterImage.prototype.update = function update(event) { 12913 var url = this.player().poster(); 12914 12915 this.setSrc(url); 12916 12917 // If there's no poster source we should display:none on this component 12918 // so it's not still clickable or right-clickable 12919 if (url) { 12920 this.show(); 12921 } else { 12922 this.hide(); 12923 } 12924 }; 12925 12926 /** 12927 * Set the source of the `PosterImage` depending on the display method. 12928 * 12929 * @param {string} url 12930 * The URL to the source for the `PosterImage`. 12931 */ 12932 12933 12934 PosterImage.prototype.setSrc = function setSrc(url) { 12935 if (this.fallbackImg_) { 12936 this.fallbackImg_.src = url; 12937 } else { 12938 var backgroundImage = ''; 12939 12940 // Any falsey values should stay as an empty string, otherwise 12941 // this will throw an extra error 12942 if (url) { 12943 backgroundImage = 'url("' + url + '")'; 12944 } 12945 12946 this.el_.style.backgroundImage = backgroundImage; 12947 } 12948 }; 12949 12950 /** 12951 * An {@link EventTarget~EventListener} for clicks on the `PosterImage`. See 12952 * {@link ClickableComponent#handleClick} for instances where this will be triggered. 12953 * 12954 * @listens tap 12955 * @listens click 12956 * @listens keydown 12957 * 12958 * @param {EventTarget~Event} event 12959 + The `click`, `tap` or `keydown` event that caused this function to be called. 12960 */ 12961 12962 12963 PosterImage.prototype.handleClick = function handleClick(event) { 12964 // We don't want a click to trigger playback when controls are disabled 12965 if (!this.player_.controls()) { 12966 return; 12967 } 12968 12969 if (this.player_.paused()) { 12970 this.player_.play(); 12971 } else { 12972 this.player_.pause(); 12973 } 12974 }; 12975 12976 return PosterImage; 12977}(_clickableComponent2['default']); 12978 12979_component2['default'].registerComponent('PosterImage', PosterImage); 12980exports['default'] = PosterImage; 12981 12982},{"3":3,"5":5,"78":78,"81":81,"83":83}],56:[function(_dereq_,module,exports){ 12983'use strict'; 12984 12985exports.__esModule = true; 12986exports.hasLoaded = exports.autoSetupTimeout = exports.autoSetup = undefined; 12987 12988var _dom = _dereq_(81); 12989 12990var Dom = _interopRequireWildcard(_dom); 12991 12992var _events = _dereq_(82); 12993 12994var Events = _interopRequireWildcard(_events); 12995 12996var _document = _dereq_(94); 12997 12998var _document2 = _interopRequireDefault(_document); 12999 13000var _window = _dereq_(95); 13001 13002var _window2 = _interopRequireDefault(_window); 13003 13004function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 13005 13006function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 13007 13008/** 13009 * @file setup.js - Functions for setting up a player without 13010 * user interaction based on the data-setup `attribute` of the video tag. 13011 * 13012 * @module setup 13013 */ 13014var _windowLoaded = false; 13015var videojs = void 0; 13016 13017/** 13018 * Set up any tags that have a data-setup `attribute` when the player is started. 13019 */ 13020var autoSetup = function autoSetup() { 13021 13022 // Protect against breakage in non-browser environments. 13023 if (!Dom.isReal()) { 13024 return; 13025 } 13026 13027 // One day, when we stop supporting IE8, go back to this, but in the meantime...*hack hack hack* 13028 // var vids = Array.prototype.slice.call(document.getElementsByTagName('video')); 13029 // var audios = Array.prototype.slice.call(document.getElementsByTagName('audio')); 13030 // var mediaEls = vids.concat(audios); 13031 13032 // Because IE8 doesn't support calling slice on a node list, we need to loop 13033 // through each list of elements to build up a new, combined list of elements. 13034 var vids = _document2['default'].getElementsByTagName('video'); 13035 var audios = _document2['default'].getElementsByTagName('audio'); 13036 var mediaEls = []; 13037 13038 if (vids && vids.length > 0) { 13039 for (var i = 0, e = vids.length; i < e; i++) { 13040 mediaEls.push(vids[i]); 13041 } 13042 } 13043 13044 if (audios && audios.length > 0) { 13045 for (var _i = 0, _e = audios.length; _i < _e; _i++) { 13046 mediaEls.push(audios[_i]); 13047 } 13048 } 13049 13050 // Check if any media elements exist 13051 if (mediaEls && mediaEls.length > 0) { 13052 13053 for (var _i2 = 0, _e2 = mediaEls.length; _i2 < _e2; _i2++) { 13054 var mediaEl = mediaEls[_i2]; 13055 13056 // Check if element exists, has getAttribute func. 13057 // IE seems to consider typeof el.getAttribute == 'object' instead of 13058 // 'function' like expected, at least when loading the player immediately. 13059 if (mediaEl && mediaEl.getAttribute) { 13060 13061 // Make sure this player hasn't already been set up. 13062 if (mediaEl.player === undefined) { 13063 var options = mediaEl.getAttribute('data-setup'); 13064 13065 // Check if data-setup attr exists. 13066 // We only auto-setup if they've added the data-setup attr. 13067 if (options !== null) { 13068 // Create new video.js instance. 13069 videojs(mediaEl); 13070 } 13071 } 13072 13073 // If getAttribute isn't defined, we need to wait for the DOM. 13074 } else { 13075 autoSetupTimeout(1); 13076 break; 13077 } 13078 } 13079 13080 // No videos were found, so keep looping unless page is finished loading. 13081 } else if (!_windowLoaded) { 13082 autoSetupTimeout(1); 13083 } 13084}; 13085 13086/** 13087 * Wait until the page is loaded before running autoSetup. This will be called in 13088 * autoSetup if `hasLoaded` returns false. 13089 * 13090 * @param {number} wait 13091 * How long to wait in ms 13092 * 13093 * @param {videojs} [vjs] 13094 * The videojs library function 13095 */ 13096function autoSetupTimeout(wait, vjs) { 13097 if (vjs) { 13098 videojs = vjs; 13099 } 13100 13101 _window2['default'].setTimeout(autoSetup, wait); 13102} 13103 13104if (Dom.isReal() && _document2['default'].readyState === 'complete') { 13105 _windowLoaded = true; 13106} else { 13107 /** 13108 * Listen for the load event on window, and set _windowLoaded to true. 13109 * 13110 * @listens load 13111 */ 13112 Events.one(_window2['default'], 'load', function () { 13113 _windowLoaded = true; 13114 }); 13115} 13116 13117/** 13118 * check if the document has been loaded 13119 */ 13120var hasLoaded = function hasLoaded() { 13121 return _windowLoaded; 13122}; 13123 13124exports.autoSetup = autoSetup; 13125exports.autoSetupTimeout = autoSetupTimeout; 13126exports.hasLoaded = hasLoaded; 13127 13128},{"81":81,"82":82,"94":94,"95":95}],57:[function(_dereq_,module,exports){ 13129'use strict'; 13130 13131exports.__esModule = true; 13132 13133var _component = _dereq_(5); 13134 13135var _component2 = _interopRequireDefault(_component); 13136 13137var _dom = _dereq_(81); 13138 13139var Dom = _interopRequireWildcard(_dom); 13140 13141var _obj = _dereq_(88); 13142 13143function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 13144 13145function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 13146 13147function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 13148 13149function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 13150 13151function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
13151onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 13152 * @file slider.js 13153 */ 13154 13155 13156/** 13157 * The base functionality for a slider. Can be vertical or horizontal. 13158 * For instance the volume bar or the seek bar on a video is a slider. 13159 * 13160 * @extends Component 13161 */ 13162var Slider = function (_Component) { 13163 _inherits(Slider, _Component); 13164 13165 /** 13166 * Create an instance of this class 13167 * 13168 * @param {Player} player 13169 * The `Player` that this class should be attached to. 13170 * 13171 * @param {Object} [options] 13172 * The key/value store of player options. 13173 */ 13174 function Slider(player, options) { 13175 _classCallCheck(this, Slider); 13176 13177 // Set property names to bar to match with the child Slider class is looking for 13178 var _this = _possibleConstructorReturn(this, _Component.call(this, player, options)); 13179 13180 _this.bar = _this.getChild(_this.options_.barName); 13181 13182 // Set a horizontal or vertical class on the slider depending on the slider type 13183 _this.vertical(!!_this.options_.vertical); 13184 13185 _this.on('mousedown', _this.handleMouseDown); 13186 _this.on('touchstart', _this.handleMouseDown); 13187 _this.on('focus', _this.handleFocus); 13188 _this.on('blur', _this.handleBlur); 13189 _this.on('click', _this.handleClick); 13190 13191 _this.on(player, 'controlsvisible', _this.update); 13192 _this.on(player, _this.playerEvent, _this.update); 13193 return _this; 13194 } 13195 13196 /** 13197 * Create the `Button`s DOM element. 13198 * 13199 * @param {string} type 13200 * Type of element to create. 13201 * 13202 * @param {Object} [props={}] 13203 * List of properties in Object form. 13204 * 13205 * @param {Object} [attributes={}] 13206 * list of attributes in Object form. 13207 * 13208 * @return {Element} 13209 * The element that gets created. 13210 */ 13211 13212 13213 Slider.prototype.createEl = function createEl(type) { 13214 var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 13215 var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; 13216 13217 // Add the slider element class to all sub classes 13218 props.className = props.className + ' vjs-slider'; 13219 props = (0, _obj.assign)({ 13220 tabIndex: 0 13221 }, props); 13222 13223 attributes = (0, _obj.assign)({ 13224 'role': 'slider', 13225 'aria-valuenow': 0, 13226 'aria-valuemin': 0, 13227 'aria-valuemax': 100, 13228 'tabIndex': 0 13229 }, attributes); 13230 13231 return _Component.prototype.createEl.call(this, type, props, attributes); 13232 }; 13233 13234 /** 13235 * Handle `mousedown` or `touchstart` events on the `Slider`. 13236 * 13237 * @param {EventTarget~Event} event 13238 * `mousedown` or `touchstart` event that triggered this function 13239 * 13240 * @listens mousedown 13241 * @listens touchstart 13242 * @fires Slider#slideractive 13243 */ 13244 13245 13246 Slider.prototype.handleMouseDown = function handleMouseDown(event) { 13247 var doc = this.bar.el_.ownerDocument; 13248 13249 event.preventDefault(); 13250 Dom.blockTextSelection(); 13251 13252 this.addClass('vjs-sliding'); 13253 /** 13254 * Triggered when the slider is in an active state 13255 * 13256 * @event Slider#slideractive 13257 * @type {EventTarget~Event} 13258 */ 13259 this.trigger('slideractive'); 13260 13261 this.on(doc, 'mousemove', this.handleMouseMove); 13262 this.on(doc, 'mouseup', this.handleMouseUp); 13263 this.on(doc, 'touchmove', this.handleMouseMove); 13264 this.on(doc, 'touchend', this.handleMouseUp); 13265 13266 this.handleMouseMove(event); 13267 }; 13268 13269 /** 13270 * Handle the `mousemove`, `touchmove`, and `mousedown` events on this `Slider`. 13271 * The `mousemove` and `touchmove` events will only only trigger this function during 13272 * `mousedown` and `touchstart`. This is due to {@link Slider#handleMouseDown} and 13273 * {@link Slider#handleMouseUp}. 13274 * 13275 * @param {EventTarget~Event} event 13276 * `mousedown`, `mousemove`, `touchstart`, or `touchmove` event that triggered 13277 * this function 13278 * 13279 * @listens mousemove 13280 * @listens touchmove 13281 */ 13282 13283 13284 Slider.prototype.handleMouseMove = function handleMouseMove(event) {}; 13285 13286 /** 13287 * Handle `mouseup` or `touchend` events on the `Slider`. 13288 * 13289 * @param {EventTarget~Event} event 13290 * `mouseup` or `touchend` event that triggered this function. 13291 * 13292 * @listens touchend 13293 * @listens mouseup 13294 * @fires Slider#sliderinactive 13295 */ 13296 13297 13298 Slider.prototype.handleMouseUp = function handleMouseUp() { 13299 var doc = this.bar.el_.ownerDocument; 13300 13301 Dom.unblockTextSelection(); 13302 13303 this.removeClass('vjs-sliding'); 13304 /** 13305 * Triggered when the slider is no longer in an active state. 13306 * 13307 * @event Slider#sliderinactive 13308 * @type {EventTarget~Event} 13309 */ 13310 this.trigger('sliderinactive'); 13311 13312 this.off(doc, 'mousemove', this.handleMouseMove); 13313 this.off(doc, 'mouseup', this.handleMouseUp); 13314 this.off(doc, 'touchmove', this.handleMouseMove); 13315 this.off(doc, 'touchend', this.handleMouseUp); 13316 13317 this.update(); 13318 }; 13319 13320 /** 13321 * Update the progress bar of the `Slider`. 13322 */ 13323 13324
13325 Slider.prototype.update = function update() { 13326 // In VolumeBar init we have a setTimeout for update that pops and update to the end of the 13327 // execution stack. The player is destroyed before then update will cause an error 13328 if (!this.el_) { 13329 return; 13330 } 13331 13332 // If scrubbing, we could use a cached value to make the handle keep up with the user's mouse. 13333 // On HTML5 browsers scrubbing is really smooth, but some flash players are slow, so we might want to utilize this later. 13334 // var progress = (this.player_.scrubbing()) ? this.player_.getCache().currentTime / this.player_.duration() : this.player_.currentTime() / this.player_.duration(); 13335 var progress = this.getPercent(); 13336 var bar = this.bar; 13337 13338 // If there's no bar... 13339 if (!bar) { 13340 return; 13341 } 13342 13343 // Protect against no duration and other division issues 13344 if (typeof progress !== 'number' || progress !== progress || progress < 0 || progress === Infinity) { 13345 progress = 0; 13346 } 13347 13348 // Convert to a percentage for setting 13349 var percentage = (progress * 100).toFixed(2) + '%'; 13350 13351 // Set the new bar width or height 13352 if (this.vertical()) { 13353 bar.el().style.height = percentage; 13354 } else { 13355 bar.el().style.width = percentage; 13356 } 13357 }; 13358 13359 /** 13360 * Calculate distance for slider 13361 * 13362 * @param {EventTarget~Event} event 13363 * The event that caused this function to run. 13364 * 13365 * @return {number} 13366 * The current position of the Slider. 13367 * - postition.x for vertical `Slider`s 13368 * - postition.y for horizontal `Slider`s 13369 */ 13370 13371 13372 Slider.prototype.calculateDistance = function calculateDistance(event) { 13373 var position = Dom.getPointerPosition(this.el_, event); 13374 13375 if (this.vertical()) { 13376 return position.y; 13377 } 13378 return position.x; 13379 }; 13380 13381 /** 13382 * Handle a `focus` event on this `Slider`. 13383 * 13384 * @param {EventTarget~Event} event 13385 * The `focus` event that caused this function to run. 13386 * 13387 * @listens focus 13388 */ 13389 13390 13391 Slider.prototype.handleFocus = function handleFocus() { 13392 this.on(this.bar.el_.ownerDocument, 'keydown', this.handleKeyPress); 13393 }; 13394 13395 /** 13396 * Handle a `keydown` event on the `Slider`. Watches for left, rigth, up, and down 13397 * arrow keys. This function will only be called when the slider has focus. See 13398 * {@link Slider#handleFocus} and {@link Slider#handleBlur}. 13399 * 13400 * @param {EventTarget~Event} event 13401 * the `keydown` event that caused this function to run. 13402 * 13403 * @listens keydown 13404 */ 13405 13406 13407 Slider.prototype.handleKeyPress = function handleKeyPress(event) { 13408 // Left and Down Arrows 13409 if (event.which === 37 || event.which === 40) { 13410 event.preventDefault(); 13411 this.stepBack(); 13412 13413 // Up and Right Arrows 13414 } else if (event.which === 38 || event.which === 39) { 13415 event.preventDefault(); 13416 this.stepForward(); 13417 } 13418 }; 13419 13420 /** 13421 * Handle a `blur` event on this `Slider`. 13422 * 13423 * @param {EventTarget~Event} event 13424 * The `blur` event that caused this function to run. 13425 * 13426 * @listens blur 13427 */ 13428 13429 Slider.prototype.handleBlur = function handleBlur() { 13430 this.off(this.bar.el_.ownerDocument, 'keydown', this.handleKeyPress); 13431 }; 13432 13433 /** 13434 * Listener for click events on slider, used to prevent clicks 13435 * from bubbling up to parent elements like button menus. 13436 * 13437 * @param {Object} event 13438 * Event that caused this object to run 13439 */ 13440 13441 13442 Slider.prototype.handleClick = function handleClick(event) { 13443 event.stopImmediatePropagation(); 13444 event.preventDefault(); 13445 }; 13446 13447 /** 13448 * Get/set if slider is horizontal for vertical 13449 * 13450 * @param {boolean} [bool] 13451 * - true if slider is vertical, 13452 * - false is horizontal 13453 * 13454 * @return {boolean|Slider} 13455 * - true if slider is vertical, and getting 13456 * - false is horizontal, and getting 13457 * - a reference to this object when setting 13458 */ 13459 13460 13461 Slider.prototype.vertical = function vertical(bool) { 13462 if (bool === undefined) { 13463 return this.vertical_ || false; 13464 } 13465 13466 this.vertical_ = !!bool; 13467 13468 if (this.vertical_) { 13469 this.addClass('vjs-slider-vertical'); 13470 } else { 13471 this.addClass('vjs-slider-horizontal'); 13472 } 13473 13474 return this; 13475 }; 13476 13477 return Slider; 13478}(_component2['default']); 13479 13480_component2['default'].registerComponent('Slider', Slider); 13481exports['default'] = Slider; 13482 13483},{"5":5,"81":81,"88":88}],58:[function(_dereq_,module,exports){ 13484'use strict'; 13485 13486exports.__esModule = true; 13487/** 13488 * @file flash-rtmp.js 13489 * @module flash-rtmp 13490 */ 13491 13492/** 13493 * Add RTMP properties to the {@link Flash} Tech. 13494 *
vendor: 4,097 bytes, lines 13495-13665
13495 * @param {Flash} Flash 13496 * The flash tech class. 13497 * 13498 * @mixin FlashRtmpDecorator 13499 */ 13500function FlashRtmpDecorator(Flash) { 13501 Flash.streamingFormats = { 13502 'rtmp/mp4': 'MP4', 13503 'rtmp/flv': 'FLV' 13504 }; 13505 13506 /** 13507 * Join connection and stream with an ampersand. 13508 * 13509 * @param {string} connection 13510 * The connection string. 13511 * 13512 * @param {string} stream 13513 * The stream string. 13514 */ 13515 Flash.streamFromParts = function (connection, stream) { 13516 return connection + '&' + stream; 13517 }; 13518 13519 /** 13520 * The flash parts object that contains connection and stream info. 13521 * 13522 * @typedef {Object} Flash~PartsObject 13523 * 13524 * @property {string} connection 13525 * The connection string of a source, defaults to an empty string. 13526 * 13527 * @property {string} stream 13528 * The stream string of the source, defaults to an empty string. 13529 */ 13530 13531 /** 13532 * Convert a source url into a stream and connection parts. 13533 * 13534 * @param {string} src 13535 * the source url 13536 * 13537 * @return {Flash~PartsObject} 13538 * The parts object that contains a connection and a stream 13539 */ 13540 Flash.streamToParts = function (src) { 13541 var parts = { 13542 connection: '', 13543 stream: '' 13544 }; 13545 13546 if (!src) { 13547 return parts; 13548 } 13549 13550 // Look for the normal URL separator we expect, '&'. 13551 // If found, we split the URL into two pieces around the 13552 // first '&'. 13553 var connEnd = src.search(/&(?!\w+=)/); 13554 var streamBegin = void 0; 13555 13556 if (connEnd !== -1) { 13557 streamBegin = connEnd + 1; 13558 } else { 13559 // If there's not a '&', we use the last '/' as the delimiter. 13560 connEnd = streamBegin = src.lastIndexOf('/') + 1; 13561 if (connEnd === 0) { 13562 // really, there's not a '/'? 13563 connEnd = streamBegin = src.length; 13564 } 13565 } 13566 13567 parts.connection = src.substring(0, connEnd); 13568 parts.stream = src.substring(streamBegin, src.length); 13569 13570 return parts; 13571 }; 13572 13573 /** 13574 * Check if the source type is a streaming type. 13575 * 13576 * @param {string} srcType 13577 * The mime type to check. 13578 * 13579 * @return {boolean} 13580 * - True if the source type is a streaming type. 13581 * - False if the source type is not a streaming type. 13582 */ 13583 Flash.isStreamingType = function (srcType) { 13584 return srcType in Flash.streamingFormats; 13585 }; 13586 13587 // RTMP has four variations, any string starting 13588 // with one of these protocols should be valid 13589 13590 /** 13591 * Regular expression used to check if the source is an rtmp source. 13592 * 13593 * @property {RegExp} Flash.RTMP_RE 13594 */ 13595 Flash.RTMP_RE = /^rtmp[set]?:\/\//i; 13596 13597 /** 13598 * Check if the source itself is a streaming type. 13599 * 13600 * @param {string} src 13601 * The url to the source. 13602 * 13603 * @return {boolean} 13604 * - True if the source url indicates that the source is streaming. 13605 * - False if the shource url indicates that the source url is not streaming. 13606 */ 13607 Flash.isStreamingSrc = function (src) { 13608 return Flash.RTMP_RE.test(src); 13609 }; 13610 13611 /** 13612 * A source handler for RTMP urls 13613 * @type {Object} 13614 */ 13615 Flash.rtmpSourceHandler = {}; 13616 13617 /** 13618 * Check if Flash can play the given mime type. 13619 * 13620 * @param {string} type 13621 * The mime type to check 13622 * 13623 * @return {string} 13624 * 'maybe', or '' (empty string) 13625 */ 13626 Flash.rtmpSourceHandler.canPlayType = function (type) { 13627 if (Flash.isStreamingType(type)) { 13628 return 'maybe'; 13629 } 13630 13631 return ''; 13632 }; 13633 13634 /** 13635 * Check if Flash can handle the source natively 13636 * 13637 * @param {Object} source 13638 * The source object 13639 * 13640 * @param {Object} [options] 13641 * The options passed to the tech 13642 * 13643 * @return {string} 13644 * 'maybe', or '' (empty string) 13645 */ 13646 Flash.rtmpSourceHandler.canHandleSource = function (source, options) { 13647 var can = Flash.rtmpSourceHandler.canPlayType(source.type); 13648 13649 if (can) { 13650 return can; 13651 } 13652 13653 if (Flash.isStreamingSrc(source.src)) { 13654 return 'maybe'; 13655 } 13656 13657 return ''; 13658 }; 13659 13660 /** 13661 * Pass the source to the flash object. 13662 * 13663 * @param {Object} source 13664 * The source object 13665 *
13666 * @param {Flash} tech 13667 * The instance of the Flash tech 13668 * 13669 * @param {Object} [options] 13670 * The options to pass to the source 13671 */ 13672 Flash.rtmpSourceHandler.handleSource = function (source, tech, options) { 13673 var srcParts = Flash.streamToParts(source.src); 13674 13675 tech.setRtmpConnection(srcParts.connection); 13676 tech.setRtmpStream(srcParts.stream); 13677 }; 13678 13679 // Register the native source handler 13680 Flash.registerSourceHandler(Flash.rtmpSourceHandler); 13681 13682 return Flash; 13683} 13684 13685exports['default'] = FlashRtmpDecorator; 13686 13687},{}],59:[function(_dereq_,module,exports){ 13688'use strict'; 13689 13690exports.__esModule = true; 13691 13692var _tech = _dereq_(62); 13693 13694var _tech2 = _interopRequireDefault(_tech); 13695 13696var _dom = _dereq_(81); 13697 13698var Dom = _interopRequireWildcard(_dom); 13699 13700var _url = _dereq_(92); 13701 13702var Url = _interopRequireWildcard(_url); 13703 13704var _timeRanges = _dereq_(90); 13705 13706var _flashRtmp = _dereq_(58); 13707 13708var _flashRtmp2 = _interopRequireDefault(_flashRtmp); 13709 13710var _component = _dereq_(5); 13711 13712var _component2 = _interopRequireDefault(_component); 13713 13714var _window = _dereq_(95); 13715 13716var _window2 = _interopRequireDefault(_window); 13717 13718var _obj = _dereq_(88); 13719 13720function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 13721 13722function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 13723 13724function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 13725 13726function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 13727 13728function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 13729 * @file flash.js 13730 * VideoJS-SWF - Custom Flash Player with HTML5-ish API 13731 * https://github.com/zencoder/video-js-swf 13732 * Not using setupTriggers. Using global onEvent func to distribute events 13733 */ 13734 13735var navigator = _window2['default'].navigator; 13736 13737/** 13738 * Flash Media Controller - Wrapper for Flash Media API 13739 * 13740 * @mixes FlashRtmpDecorator 13741 * @mixes Tech~SouceHandlerAdditions 13742 * @extends Tech 13743 */ 13744 13745var Flash = function (_Tech) { 13746 _inherits(Flash, _Tech); 13747 13748 /** 13749 * Create an instance of this Tech. 13750 * 13751 * @param {Object} [options] 13752 * The key/value store of player options. 13753 * 13754 * @param {Component~ReadyCallback} ready 13755 * Callback function to call when the `Flash` Tech is ready. 13756 */ 13757 function Flash(options, ready) { 13758 _classCallCheck(this, Flash); 13759 13760 // Set the source when ready 13761 var _this = _possibleConstructorReturn(this, _Tech.call(this, options, ready)); 13762 13763 if (options.source) { 13764 _this.ready(function () { 13765 this.setSource(options.source); 13766 }, true); 13767 } 13768 13769 // Having issues with Flash reloading on certain page actions (hide/resize/fullscreen) in certain browsers 13770 // This allows resetting the playhead when we catch the reload 13771 if (options.startTime) { 13772 _this.ready(function () { 13773 this.load(); 13774 this.play(); 13775 this.currentTime(options.startTime); 13776 }, true); 13777 } 13778 13779 // Add global window functions that the swf expects 13780 // A 4.x workflow we weren't able to solve for in 5.0 13781 // because of the need to hard code these functions 13782 // into the swf for security reasons 13783 _window2['default'].videojs = _window2['default'].videojs || {}; 13784 _window2['default'].videojs.Flash = _window2['default'].videojs.Flash || {}; 13785 _window2['default'].videojs.Flash.onReady = Flash.onReady; 13786 _window2['default'].videojs.Flash.onEvent = Flash.onEvent; 13787 _window2['default'].videojs.Flash.onError = Flash.onError; 13788 13789 _this.on('seeked', function () { 13790 this.lastSeekTarget_ = undefined; 13791 }); 13792 13793 return _this; 13794 } 13795 13796 /** 13797 * Create the `Flash` Tech's DOM element. 13798 * 13799 * @return {Element} 13800 * The element that gets created. 13801 */ 13802 13803 13804 Flash.prototype.createEl = function createEl() { 13805 var options = this.options_; 13806 13807 // If video.js is hosted locally you should also set the location 13808 // for the hosted swf, which should be relative to the page (not video.js) 13809 // Otherwise this adds a CDN url. 13810 // The CDN also auto-adds a swf URL for that specific version. 13811 if (!options.swf) { 13812 var ver = '5.4.1'; 13813 13814 options.swf = '//vjs.zencdn.net/swf/' + ver + '/video-js.swf'; 13815 } 13816 13817 // Generate ID for swf object 13818 var objId = options.techId; 13819 13820 // Merge default flashvars with ones passed in to init 13821 var flashVars = (0, _obj.assign)({ 13822 13823 // SWF Callback Functions 13824 readyFunction: 'videojs.Flash.onReady', 13825 eventProxyFunction: 'videojs.Flash.onEvent', 13826 errorEventProxyFunction: 'videojs.Flash.onError', 13827 13828 // Player Settings 13829 autoplay: options.autoplay, 13830 preload: options.preload, 13831 loop: options.loop, 13832 muted: options.muted 13833 13834 }, options.flashVars); 13835 13836 // Merge default parames with ones passed in 13837 var params = (0, _obj.assign)({ 13838 // Opaque is needed to overlay controls, but can affect playback performance 13839 wmode: 'opaque', 13840 // Using bgcolor prevents a white flash when the object is loading 13841 bgcolor: '#000000' 13842 }, options.params); 13843 13844 // Merge default attributes with ones passed in 13845 var attributes = (0, _obj.assign)({ 13846 // Both ID and Name needed or swf to identify itself 13847 id: objId, 13848 name: objId, 13849 'class': 'vjs-tech' 13850 }, options.attributes); 13851 13852 this.el_ = Flash.embed(options.swf, flashVars, params, attributes); 13853 this.el_.tech = this; 13854 13855 return this.el_; 13856 }; 13857 13858 /** 13859 * Called by {@link Player#play} to play using the `Flash` `Tech`. 13860 */ 13861 13862 13863 Flash.prototype.play = function play() { 13864 if (this.ended()) { 13865 this.setCurrentTime(0); 13866 } 13867 this.el_.vjs_play(); 13868 }; 13869 13870 /** 13871 * Called by {@link Player#pause} to pause using the `Flash` `Tech`. 13872 */ 13873 13874 13875 Flash.prototype.pause = function pause() { 13876 this.el_.vjs_pause(); 13877 }; 13878 13879 /** 13880 * A getter/setter for the `Flash` Tech's source object. 13881 * > Note: Please use {@link Flash#setSource} 13882 * 13883 * @param {Tech~SourceObject} [src] 13884 * The source object you want to set on the `Flash` techs. 13885 * 13886 * @return {Tech~SourceObject|undefined} 13887 * - The current source object when a source is not passed in. 13888 * - undefined when setting 13889 * 13890 * @deprecated Since version 5. 13891 */ 13892 13893 13894 Flash.prototype.src = function src(_src) { 13895 if (_src === undefined) { 13896 return this.currentSrc(); 13897 } 13898 13899 // Setting src through `src` not `setSrc` will be deprecated 13900 return this.setSrc(_src); 13901 }; 13902 13903 /** 13904 * A getter/setter for the `Flash` Tech's source object. 13905 * 13906 * @param {Tech~SourceObject} [src] 13907 * The source object you want to set on the `Flash` techs. 13908 * 13909 * @return {Tech~SourceObject|undefined} 13910 * - The current source object when a source is not passed in. 13911 * - undefined when setting 13912 */ 13913 13914 13915 Flash.prototype.setSrc = function setSrc(src) { 13916 var _this2 = this; 13917 13918 // Make sure source URL is absolute. 13919 src = Url.getAbsoluteURL(src); 13920 this.el_.vjs_src(src); 13921 13922 // Currently the SWF doesn't autoplay if you load a source later. 13923 // e.g. Load player w/ no source, wait 2s, set src. 13924 if (this.autoplay()) { 13925 this.setTimeout(function () { 13926 return _this2.play(); 13927 }, 0); 13928 } 13929 }; 13930 13931 /** 13932 * Indicates whether the media is currently seeking to a new position or not. 13933 * 13934 * @return {boolean} 13935 * - True if seeking to a new position 13936 * - False otherwise 13937 */ 13938 13939 13940 Flash.prototype.seeking = function seeking() { 13941 return this.lastSeekTarget_ !== undefined; 13942 }; 13943 13944 /** 13945 * Returns the current time in seconds that the media is at in playback. 13946 * 13947 * @param {number} time 13948 * Current playtime of the media in seconds. 13949 */ 13950 13951 13952 Flash.prototype.setCurrentTime = function setCurrentTime(time) { 13953 var seekable = this.seekable(); 13954 13955 if (seekable.length) { 13956 // clamp to the current seekable range 13957 time = time > seekable.start(0) ? time : seekable.start(0); 13958 time = time < seekable.end(seekable.length - 1) ? time : seekable.end(seekable.length - 1); 13959 13960 this.lastSeekTarget_ = time; 13961 this.trigger('seeking'); 13962 this.el_.vjs_setProperty('currentTime', time); 13963 _Tech.prototype.setCurrentTime.call(this); 13964 } 13965 }; 13966 13967 /** 13968 * Get the current playback time in seconds 13969 * 13970 * @return {number} 13971 * The current time of playback in seconds. 13972 */ 13973 13974 13975 Flash.prototype.currentTime = function currentTime() { 13976 // when seeking make the reported time keep up with the requested time 13977 // by reading the time we're seeking to 13978 if (this.seeking()) { 13979 return this.lastSeekTarget_ || 0; 13980 } 13981 return this.el_.vjs_getProperty('currentTime'); 13982 }; 13983 13984 /** 13985 * Get the current source 13986 * 13987 * @method currentSrc 13988 * @return {Tech~SourceObject} 13989 * The current source 13990 */ 13991 13992 13993 Flash.prototype.currentSrc = function currentSrc() { 13994 if (this.currentSource_) { 13995 return this.currentSource_.src; 13996 } 13997 return this.el_.vjs_getProperty('currentSrc'); 13998 }; 13999 14000 /** 14001 * Get the total duration of the current media. 14002 * 14003 * @return {number} 14004 8 The total duration of the current media. 14005 */ 14006 14007 14008 Flash.prototype.duration = function duration() { 14009 if (this.readyState() === 0) { 14010 return NaN; 14011 } 14012 var duration = this.el_.vjs_getProperty('duration'); 14013 14014 return duration >= 0 ? duration : Infinity; 14015 }; 14016 14017 /** 14018 * Load media into Tech. 14019 */ 14020 14021 14022 Flash.prototype.load = function load() { 14023 this.el_.vjs_load(); 14024 }; 14025 14026 /** 14027 * Get the poster image that was set on the tech. 14028 */ 14029 14030 14031 Flash.prototype.poster = function poster() { 14032 this.el_.vjs_getProperty('poster'); 14033 }; 14034 14035 /** 14036 * Poster images are not handled by the Flash tech so make this is a no-op. 14037 */ 14038 14039 14040 Flash.prototype.setPoster = function setPoster() {}; 14041 14042 /** 14043 * Determine the time ranges that can be seeked to in the media. 14044 * 14045 * @return {TimeRange} 14046 * Returns the time ranges that can be seeked to. 14047 */ 14048 14049 14050 Flash.prototype.seekable = function seekable() { 14051 var duration = this.duration(); 14052 14053 if (duration === 0) { 14054 return (0, _timeRanges.createTimeRange)(); 14055 } 14056 return (0, _timeRanges.createTimeRange)(0, duration); 14057 }; 14058 14059 /** 14060 * Get and create a `TimeRange` object for buffering. 14061 * 14062 * @return {TimeRange} 14063 * The time range object that was created. 14064 */ 14065 14066 14067 Flash.prototype.buffered = function buffered() { 14068 var ranges = this.el_.vjs_getProperty('buffered'); 14069 14070 if (ranges.length === 0) { 14071 return (0, _timeRanges.createTimeRange)(); 14072 } 14073 return (0, _timeRanges.createTimeRange)(ranges[0][0], ranges[0][1]); 14074 }; 14075 14076 /** 14077 * Get fullscreen support - 14078 * 14079 * Flash does not allow fullscreen through javascript 14080 * so this always returns false. 14081 * 14082 * @return {boolean}
14083 * The Flash tech does not support fullscreen, so it will always return false. 14084 */ 14085 14086 14087 Flash.prototype.supportsFullScreen = function supportsFullScreen() { 14088 // Flash does not allow fullscreen through javascript 14089 return false; 14090 }; 14091 14092 /** 14093 * Flash does not allow fullscreen through javascript 14094 * so this always returns false. 14095 * 14096 * @return {boolean} 14097 * The Flash tech does not support fullscreen, so it will always return false. 14098 */ 14099 14100 14101 Flash.prototype.enterFullScreen = function enterFullScreen() { 14102 return false; 14103 }; 14104 14105 /** 14106 * Gets available media playback quality metrics as specified by the W3C's Media 14107 * Playback Quality API. 14108 * 14109 * @see [Spec]{@link https://wicg.github.io/media-playback-quality} 14110 * 14111 * @return {Object} 14112 * An object with supported media playback quality metrics 14113 */ 14114 14115 14116 Flash.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() { 14117 var videoPlaybackQuality = this.el_.vjs_getProperty('getVideoPlaybackQuality'); 14118 14119 if (_window2['default'].performance && typeof _window2['default'].performance.now === 'function') { 14120 videoPlaybackQuality.creationTime = _window2['default'].performance.now(); 14121 } else if (_window2['default'].performance && _window2['default'].performance.timing && typeof _window2['default'].performance.timing.navigationStart === 'number') { 14122 videoPlaybackQuality.creationTime = _window2['default'].Date.now() - _window2['default'].performance.timing.navigationStart; 14123 } 14124 14125 return videoPlaybackQuality; 14126 }; 14127 14128 return Flash; 14129}(_tech2['default']); 14130 14131// Create setters and getters for attributes 14132 14133 14134var _api = Flash.prototype; 14135var _readWrite = 'rtmpConnection,rtmpStream,preload,defaultPlaybackRate,playbackRate,autoplay,loop,mediaGroup,controller,controls,volume,muted,defaultMuted'.split(','); 14136var _readOnly = 'networkState,readyState,initialTime,startOffsetTime,paused,ended,videoWidth,videoHeight'.split(','); 14137 14138function _createSetter(attr) { 14139 var attrUpper = attr.charAt(0).toUpperCase() + attr.slice(1); 14140 14141 _api['set' + attrUpper] = function (val) { 14142 return this.el_.vjs_setProperty(attr, val); 14143 }; 14144}
vendor: 12,234 bytes, lines 14145-14546
14145 14146function _createGetter(attr) { 14147 _api[attr] = function () { 14148 return this.el_.vjs_getProperty(attr); 14149 }; 14150} 14151 14152// Create getter and setters for all read/write attributes 14153for (var i = 0; i < _readWrite.length; i++) { 14154 _createGetter(_readWrite[i]); 14155 _createSetter(_readWrite[i]); 14156} 14157 14158// Create getters for read-only attributes 14159for (var _i = 0; _i < _readOnly.length; _i++) { 14160 _createGetter(_readOnly[_i]); 14161} 14162 14163/** ------------------------------ Getters ------------------------------ **/ 14164/** 14165 * Get the value of `rtmpConnection` from the swf. 14166 * 14167 * @method Flash#rtmpConnection 14168 * @return {string} 14169 * The current value of `rtmpConnection` on the swf. 14170 */ 14171 14172/** 14173 * Get the value of `rtmpStream` from the swf. 14174 * 14175 * @method Flash#rtmpStream 14176 * @return {string} 14177 * The current value of `rtmpStream` on the swf. 14178 */ 14179 14180/** 14181 * Get the value of `preload` from the swf. `preload` indicates 14182 * what should download before the media is interacted with. It can have the following 14183 * values: 14184 * - none: nothing should be downloaded 14185 * - metadata: poster and the first few frames of the media may be downloaded to get 14186 * media dimensions and other metadata 14187 * - auto: allow the media and metadata for the media to be downloaded before 14188 * interaction 14189 * 14190 * @method Flash#preload 14191 * @return {string} 14192 * The value of `preload` from the swf. Will be 'none', 'metadata', 14193 * or 'auto'. 14194 */ 14195 14196/** 14197 * Get the value of `defaultPlaybackRate` from the swf. 14198 * 14199 * @method Flash#defaultPlaybackRate 14200 * @return {number} 14201 * The current value of `defaultPlaybackRate` on the swf. 14202 */ 14203 14204/** 14205 * Get the value of `playbackRate` from the swf. `playbackRate` indicates 14206 * the rate at which the media is currently playing back. Examples: 14207 * - if playbackRate is set to 2, media will play twice as fast. 14208 * - if playbackRate is set to 0.5, media will play half as fast. 14209 * 14210 * @method Flash#playbackRate 14211 * @return {number} 14212 * The value of `playbackRate` from the swf. A number indicating 14213 * the current playback speed of the media, where 1 is normal speed. 14214 */ 14215 14216/** 14217 * Get the value of `autoplay` from the swf. `autoplay` indicates 14218 * that the media should start to play as soon as the page is ready. 14219 * 14220 * @method Flash#autoplay 14221 * @return {boolean} 14222 * - The value of `autoplay` from the swf. 14223 * - True indicates that the media ashould start as soon as the page loads. 14224 * - False indicates that the media should not start as soon as the page loads. 14225 */ 14226 14227/** 14228 * Get the value of `loop` from the swf. `loop` indicates 14229 * that the media should return to the start of the media and continue playing once 14230 * it reaches the end. 14231 * 14232 * @method Flash#loop 14233 * @return {boolean} 14234 * - The value of `loop` from the swf. 14235 * - True indicates that playback should seek back to start once 14236 * the end of a media is reached. 14237 * - False indicates that playback should not loop back to the start when the 14238 * end of the media is reached. 14239 */ 14240 14241/** 14242 * Get the value of `mediaGroup` from the swf. 14243 * 14244 * @method Flash#mediaGroup 14245 * @return {string} 14246 * The current value of `mediaGroup` on the swf. 14247 */ 14248 14249/** 14250 * Get the value of `controller` from the swf. 14251 * 14252 * @method Flash#controller 14253 * @return {string} 14254 * The current value of `controller` on the swf. 14255 */ 14256 14257/** 14258 * Get the value of `controls` from the swf. `controls` indicates 14259 * whether the native flash controls should be shown or hidden. 14260 * 14261 * @method Flash#controls 14262 * @return {boolean} 14263 * - The value of `controls` from the swf. 14264 * - True indicates that native controls should be showing. 14265 * - False indicates that native controls should be hidden. 14266 */ 14267 14268/** 14269 * Get the value of the `volume` from the swf. `volume` indicates the current 14270 * audio level as a percentage in decimal form. This means that 1 is 100%, 0.5 is 50%, and 14271 * so on. 14272 * 14273 * @method Flash#volume 14274 * @return {number} 14275 * The volume percent as a decimal. Value will be between 0-1. 14276 */ 14277 14278/** 14279 * Get the value of the `muted` from the swf. `muted` indicates the current 14280 * audio level should be silent. 14281 * 14282 * @method Flash#muted 14283 * @return {boolean} 14284 * - True if the audio should be set to silent 14285 * - False otherwise 14286 */ 14287 14288/** 14289 * Get the value of `defaultMuted` from the swf. `defaultMuted` indicates 14290 * whether the media should start muted or not. Only changes the default state of the 14291 * media. `muted` and `defaultMuted` can have different values. `muted` indicates the 14292 * current state. 14293 * 14294 * @method Flash#defaultMuted 14295 * @return {boolean} 14296 * - The value of `defaultMuted` from the swf. 14297 * - True indicates that the media should start muted. 14298 * - False indicates that the media should not start muted. 14299 */ 14300 14301/** 14302 * Get the value of `networkState` from the swf. `networkState` indicates 14303 * the current network state. It returns an enumeration from the following list: 14304 * - 0: NETWORK_EMPTY 14305 * - 1: NEWORK_IDLE 14306 * - 2: NETWORK_LOADING 14307 * - 3: NETWORK_NO_SOURCE 14308 * 14309 * @method Flash#networkState 14310 * @return {number} 14311 * The value of `networkState` from the swf. This will be a number 14312 * from the list in the description. 14313 */ 14314 14315/** 14316 * Get the value of `readyState` from the swf. `readyState` indicates 14317 * the current state of the media element. It returns an enumeration from the 14318 * following list: 14319 * - 0: HAVE_NOTHING 14320 * - 1: HAVE_METADATA 14321 * - 2: HAVE_CURRENT_DATA 14322 * - 3: HAVE_FUTURE_DATA 14323 * - 4: HAVE_ENOUGH_DATA 14324 * 14325 * @method Flash#readyState 14326 * @return {number} 14327 * The value of `readyState` from the swf. This will be a number 14328 * from the list in the description. 14329 */ 14330 14331/** 14332 * Get the value of `readyState` from the swf. `readyState` indicates 14333 * the current state of the media element. It returns an enumeration from the 14334 * following list: 14335 * - 0: HAVE_NOTHING 14336 * - 1: HAVE_METADATA 14337 * - 2: HAVE_CURRENT_DATA 14338 * - 3: HAVE_FUTURE_DATA 14339 * - 4: HAVE_ENOUGH_DATA 14340 * 14341 * @method Flash#readyState 14342 * @return {number} 14343 * The value of `readyState` from the swf. This will be a number 14344 * from the list in the description. 14345 */ 14346 14347/** 14348 * Get the value of `initialTime` from the swf. 14349 * 14350 * @method Flash#initialTime 14351 * @return {number} 14352 * The `initialTime` proprety on the swf. 14353 */ 14354 14355/** 14356 * Get the value of `startOffsetTime` from the swf. 14357 * 14358 * @method Flash#startOffsetTime 14359 * @return {number} 14360 * The `startOffsetTime` proprety on the swf. 14361 */ 14362 14363/** 14364 * Get the value of `paused` from the swf. `paused` indicates whether the swf 14365 * is current paused or not. 14366 * 14367 * @method Flash#paused 14368 * @return {boolean} 14369 * The value of `paused` from the swf. 14370 */ 14371 14372/** 14373 * Get the value of `ended` from the swf. `ended` indicates whether 14374 * the media has reached the end or not. 14375 * 14376 * @method Flash#ended 14377 * @return {boolean} 14378 * - True indicates that the media has ended. 14379 * - False indicates that the media has not ended. 14380 * 14381 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-ended} 14382 */ 14383 14384/** 14385 * Get the value of `videoWidth` from the swf. `videoWidth` indicates 14386 * the current width of the media in css pixels. 14387 * 14388 * @method Flash#videoWidth 14389 * @return {number} 14390 * The value of `videoWidth` from the swf. This will be a number 14391 * in css pixels. 14392 */ 14393 14394/** 14395 * Get the value of `videoHeight` from the swf. `videoHeigth` indicates 14396 * the current height of the media in css pixels. 14397 * 14398 * @method Flassh.prototype.videoHeight 14399 * @return {number} 14400 * The value of `videoHeight` from the swf. This will be a number 14401 * in css pixels. 14402 */ 14403/** ------------------------------ Setters ------------------------------ **/ 14404 14405/** 14406 * Set the value of `rtmpConnection` on the swf. 14407 * 14408 * @method Flash#setRtmpConnection 14409 * @param {string} rtmpConnection 14410 * New value to set the `rtmpConnection` property to. 14411 */ 14412 14413/** 14414 * Set the value of `rtmpStream` on the swf. 14415 * 14416 * @method Flash#setRtmpStream 14417 * @param {string} rtmpStream 14418 * New value to set the `rtmpStream` property to. 14419 */ 14420 14421/** 14422 * Set the value of `preload` on the swf. `preload` indicates 14423 * what should download before the media is interacted with. It can have the following 14424 * values: 14425 * - none: nothing should be downloaded 14426 * - metadata: poster and the first few frames of the media may be downloaded to get 14427 * media dimensions and other metadata 14428 * - auto: allow the media and metadata for the media to be downloaded before 14429 * interaction 14430 * 14431 * @method Flash#setPreload 14432 * @param {string} preload 14433 * The value of `preload` to set on the swf. Should be 'none', 'metadata', 14434 * or 'auto'. 14435 */ 14436 14437/** 14438 * Set the value of `defaultPlaybackRate` on the swf. 14439 * 14440 * @method Flash#setDefaultPlaybackRate 14441 * @param {number} defaultPlaybackRate 14442 * New value to set the `defaultPlaybackRate` property to. 14443 */ 14444 14445/** 14446 * Set the value of `playbackRate` on the swf. `playbackRate` indicates 14447 * the rate at which the media is currently playing back. Examples: 14448 * - if playbackRate is set to 2, media will play twice as fast. 14449 * - if playbackRate is set to 0.5, media will play half as fast. 14450 * 14451 * @method Flash#setPlaybackRate 14452 * @param {number} playbackRate 14453 * New value of `playbackRate` on the swf. A number indicating 14454 * the current playback speed of the media, where 1 is normal speed. 14455 */ 14456 14457/** 14458 * Set the value of `autoplay` on the swf. `autoplay` indicates 14459 * that the media should start to play as soon as the page is ready. 14460 * 14461 * @method Flash#setAutoplay 14462 * @param {boolean} autoplay 14463 * - The value of `autoplay` from the swf. 14464 * - True indicates that the media ashould start as soon as the page loads. 14465 * - False indicates that the media should not start as soon as the page loads. 14466 */ 14467 14468/** 14469 * Set the value of `loop` on the swf. `loop` indicates 14470 * that the media should return to the start of the media and continue playing once 14471 * it reaches the end. 14472 * 14473 * @method Flash#setLoop 14474 * @param {boolean} loop 14475 * - True indicates that playback should seek back to start once 14476 * the end of a media is reached. 14477 * - False indicates that playback should not loop back to the start when the 14478 * end of the media is reached. 14479 */ 14480 14481/** 14482 * Set the value of `mediaGroup` on the swf. 14483 * 14484 * @method Flash#setMediaGroup 14485 * @param {string} mediaGroup 14486 * New value of `mediaGroup` to set on the swf. 14487 */ 14488 14489/** 14490 * Set the value of `controller` on the swf. 14491 * 14492 * @method Flash#setController 14493 * @param {string} controller 14494 * New value the current value of `controller` on the swf. 14495 */ 14496 14497/** 14498 * Get the value of `controls` from the swf. `controls` indicates 14499 * whether the native flash controls should be shown or hidden. 14500 * 14501 * @method Flash#controls 14502 * @return {boolean} 14503 * - The value of `controls` from the swf. 14504 * - True indicates that native controls should be showing. 14505 * - False indicates that native controls should be hidden. 14506 */ 14507 14508/** 14509 * Set the value of the `volume` on the swf. `volume` indicates the current 14510 * audio level as a percentage in decimal form. This means that 1 is 100%, 0.5 is 50%, and 14511 * so on. 14512 * 14513 * @method Flash#setVolume 14514 * @param {number} percentAsDecimal 14515 * The volume percent as a decimal. Value will be between 0-1. 14516 */ 14517 14518/** 14519 * Set the value of the `muted` on the swf. `muted` indicates that the current 14520 * audio level should be silent. 14521 * 14522 * @method Flash#setMuted 14523 * @param {boolean} muted 14524 * - True if the audio should be set to silent 14525 * - False otherwise 14526 */ 14527 14528/** 14529 * Set the value of `defaultMuted` on the swf. `defaultMuted` indicates 14530 * whether the media should start muted or not. Only changes the default state of the 14531 * media. `muted` and `defaultMuted` can have different values. `muted` indicates the 14532 * current state. 14533 * 14534 * @method Flash#setDefaultMuted 14535 * @param {boolean} defaultMuted 14536 * - True indicates that the media should start muted. 14537 * - False indicates that the media should not start muted. 14538 */ 14539 14540/* Flash Support Testing -------------------------------------------------------- */ 14541 14542/** 14543 * Check if the Flash tech is currently supported. 14544 * 14545 * @return {boolean} 14546 * - True
14546if the flash tech is supported. 14547 * - False otherwise. 14548 */ 14549Flash.isSupported = function () { 14550 return Flash.version()[0] >= 10; 14551 // return swfobject.hasFlashPlayerVersion('10'); 14552}; 14553 14554// Add Source Handler pattern functions to this tech 14555_tech2['default'].withSourceHandlers(Flash); 14556 14557/* 14558 * Native source handler for flash, simply passes the source to the swf element. 14559 * 14560 * @property {Tech~SourceObject} source 14561 * The source object 14562 * 14563 * @property {Flash} tech 14564 * The instance of the Flash tech 14565 */ 14566Flash.nativeSourceHandler = {}; 14567 14568/** 14569 * Check if the Flash can play the given mime type. 14570 * 14571 * @param {string} type 14572 * The mimetype to check 14573 * 14574 * @return {string} 14575 * 'maybe', or '' (empty string) 14576 */ 14577Flash.nativeSourceHandler.canPlayType = function (type) { 14578 if (type in Flash.formats) { 14579 return 'maybe'; 14580 } 14581 14582 return ''; 14583}; 14584 14585/** 14586 * Check if the media element can handle a source natively. 14587 * 14588 * @param {Tech~SourceObject} source 14589 * The source object 14590 * 14591 * @param {Object} [options] 14592 * Options to be passed to the tech. 14593 * 14594 * @return {string} 14595 * 'maybe', or '' (empty string). 14596 */ 14597Flash.nativeSourceHandler.canHandleSource = function (source, options) { 14598 var type = void 0; 14599 14600 function guessMimeType(src) { 14601 var ext = Url.getFileExtension(src); 14602 14603 if (ext) { 14604 return 'video/' + ext; 14605 } 14606 return ''; 14607 } 14608 14609 if (!source.type) { 14610 type = guessMimeType(source.src); 14611 } else { 14612 // Strip code information from the type because we don't get that specific 14613 type = source.type.replace(/;.*/, '').toLowerCase(); 14614 } 14615 14616 return Flash.nativeSourceHandler.canPlayType(type); 14617}; 14618 14619/** 14620 * Pass the source to the swf. 14621 * 14622 * @param {Tech~SourceObject} source 14623 * The source object 14624 * 14625 * @param {Flash} tech 14626 * The instance of the Flash tech 14627 * 14628 * @param {Object} [options] 14629 * The options to pass to the source 14630 */ 14631Flash.nativeSourceHandler.handleSource = function (source, tech, options) { 14632 tech.setSrc(source.src); 14633}; 14634 14635/** 14636 * noop for native source handler dispose, as cleanup will happen automatically. 14637 */ 14638Flash.nativeSourceHandler.dispose = function () {}; 14639 14640// Register the native source handler 14641Flash.registerSourceHandler(Flash.nativeSourceHandler); 14642 14643/** 14644 * Flash supported mime types. 14645 * 14646 * @constant {Object} 14647 */ 14648Flash.formats = { 14649 'video/flv': 'FLV', 14650 'video/x-flv': 'FLV', 14651 'video/mp4': 'MP4', 14652 'video/m4v': 'MP4' 14653}; 14654 14655/** 14656 * Called when the the swf is "ready", and makes sure that the swf is really 14657 * ready using {@link Flash#checkReady} 14658 */ 14659Flash.onReady = function (currSwf) { 14660 var el = Dom.getEl(currSwf); 14661 var tech = el && el.tech; 14662 14663 // if there is no el then the tech has been disposed 14664 // and the tech element was removed from the player div 14665 if (tech && tech.el()) { 14666 // check that the flash object is really ready 14667 Flash.checkReady(tech); 14668 } 14669}; 14670 14671/** 14672 * The SWF isn't always ready when it says it is. Sometimes the API functions still 14673 * need to be added to the object. If it's not ready, we set a timeout to check again 14674 * shortly. 14675 * 14676 * @param {Flash} tech 14677 * The instance of the flash tech to check. 14678 */ 14679Flash.checkReady = function (tech) { 14680 // stop worrying if the tech has been disposed 14681 if (!tech.el()) { 14682 return; 14683 } 14684 14685 // check if API property exists 14686 if (tech.el().vjs_getProperty) { 14687 // tell tech it's ready 14688 tech.triggerReady(); 14689 } else { 14690 // wait longer 14691 this.setTimeout(function () { 14692 Flash.checkReady(tech); 14693 }, 50); 14694 } 14695}; 14696 14697/** 14698 * Trigger events from the swf on the Flash Tech. 14699 * 14700 * @param {number} swfID 14701 * The id of the swf that had the event 14702 * 14703 * @param {string} eventName 14704 * The name of the event to trigger 14705 */ 14706Flash.onEvent = function (swfID, eventName) { 14707 var tech = Dom.getEl(swfID).tech; 14708 var args = Array.prototype.slice.call(arguments, 2); 14709 14710 // dispatch Flash events asynchronously for two reasons: 14711 // - Flash swallows any exceptions generated by javascript it 14712 // invokes 14713 // - Flash is suspended until the javascript returns which may cause 14714 // playback performance issues 14715 tech.setTimeout(function () { 14716 tech.trigger(eventName, args); 14717 }, 1); 14718}; 14719 14720/** 14721 * Log errors from the swf on the Flash tech. 14722 * 14723 * @param {number} swfID 14724 * The id of the swf that had an error. 14725 * 14726 * @param {string} The error string 14727 * The error to set on the Flash Tech. 14728 * 14729 * @return {MediaError|undefined} 14730 * - Returns a MediaError when err is 'srcnotfound' 14731 * - Returns undefined otherwise. 14732 */ 14733Flash.onError = function (swfID, err) { 14734 var tech = Dom.getEl(swfID).tech; 14735 14736 // trigger MEDIA_ERR_SRC_NOT_SUPPORTED 14737 if (err === 'srcnotfound') { 14738 return tech.error(4); 14739 } 14740 14741 // trigger a custom error 14742 tech.error('FLASH: ' + err); 14743}; 14744 14745/** 14746 * Get the current version of Flash that is in use on the page. 14747 * 14748 * @return {Array} 14749 * an array of versions that are available. 14750 */ 14751Flash.version = function () { 14752 var version = '0,0,0'; 14753 14754 // IE 14755 try { 14756 version = new _window2['default'].ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version').replace(/\D+/g, ',').match(/^,?(.+),?$/)[1]; 14757 14758 // other browsers 14759 } catch (e) { 14760 try { 14761 if (navigator.mimeTypes['application/x-shockwave-flash'].enabledPlugin) { 14762 version = (navigator.plugins['Shockwave Flash 2.0'] || navigator.plugins['Shockwave Flash']).description.replace(/\D+/g, ',').match(/^,?(.+),?$/)[1]; 14763 } 14764 } catch (err) { 14765 // satisfy linter 14766 } 14767 } 14768 return version.split(','); 14769}; 14770 14771/** 14772 * Only use for non-iframe embeds. 14773 * 14774 * @param {Object} swf 14775 * The videojs-swf object. 14776 * 14777 * @param {Object} flashVars 14778 * Names and values to use as flash option variables. 14779 * 14780 * @param {Object} params
14781 * Style parameters to set on the object. 14782 * 14783 * @param {Object} attributes 14784 * Attributes to set on the element. 14785 * 14786 * @return {Element} 14787 * The embeded Flash DOM element. 14788 */ 14789Flash.embed = function (swf, flashVars, params, attributes) { 14790 var code = Flash.getEmbedCode(swf, flashVars, params, attributes); 14791 14792 // Get element by embedding code and retrieving created element 14793 var obj = Dom.createEl('div', { innerHTML: code }).childNodes[0]; 14794 14795 return obj; 14796}; 14797 14798/** 14799 * Only use for non-iframe embeds. 14800 * 14801 * @param {Object} swf 14802 * The videojs-swf object. 14803 * 14804 * @param {Object} flashVars 14805 * Names and values to use as flash option variables. 14806 * 14807 * @param {Object} params 14808 * Style parameters to set on the object. 14809 * 14810 * @param {Object} attributes 14811 * Attributes to set on the element. 14812 * 14813 * @return {Element} 14814 * The embeded Flash DOM element. 14815 */ 14816Flash.getEmbedCode = function (swf, flashVars, params, attributes) { 14817 var objTag = '<object type="application/x-shockwave-flash" '; 14818 var flashVarsString = ''; 14819 var paramsString = ''; 14820 var attrsString = ''; 14821 14822 // Convert flash vars to string 14823 if (flashVars) { 14824 Object.getOwnPropertyNames(flashVars).forEach(function (key) { 14825 flashVarsString += key + '=' + flashVars[key] + '&'; 14826 }); 14827 } 14828 14829 // Add swf, flashVars, and other default params 14830 params = (0, _obj.assign)({ 14831 movie: swf, 14832 flashvars: flashVarsString, 14833 // Required to talk to swf 14834 allowScriptAccess: 'always', 14835 // All should be default, but having security issues. 14836 allowNetworking: 'all' 14837 }, params); 14838 14839 // Create param tags string 14840 Object.getOwnPropertyNames(params).forEach(function (key) { 14841 paramsString += '<param name="' + key + '" value="' + params[key] + '" />'; 14842 }); 14843 14844 attributes = (0, _obj.assign)({ 14845 // Add swf to attributes (need both for IE and Others to work) 14846 data: swf, 14847 14848 // Default to 100% width/height 14849 width: '100%', 14850 height: '100%' 14851 14852 }, attributes); 14853 14854 // Create Attributes string 14855 Object.getOwnPropertyNames(attributes).forEach(function (key) { 14856 attrsString += key + '="' + attributes[key] + '" '; 14857 }); 14858 14859 return '' + objTag + attrsString + '>' + paramsString + '</object>'; 14860}; 14861 14862// Run Flash through the RTMP decorator 14863(0, _flashRtmp2['default'])(Flash); 14864 14865_component2['default'].registerComponent('Flash', Flash); 14866_tech2['default'].registerTech('Flash', Flash); 14867exports['default'] = Flash; 14868 14869},{"5":5,"58":58,"62":62,"81":81,"88":88,"90":90,"92":92,"95":95}],60:[function(_dereq_,module,exports){ 14870'use strict'; 14871 14872exports.__esModule = true; 14873 14874var _templateObject = _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.']); 14875 14876var _tech = _dereq_(62); 14877 14878var _tech2 = _interopRequireDefault(_tech); 14879 14880var _component = _dereq_(5); 14881 14882var _component2 = _interopRequireDefault(_component); 14883 14884var _dom = _dereq_(81); 14885 14886var Dom = _interopRequireWildcard(_dom); 14887 14888var _url = _dereq_(92); 14889 14890var Url = _interopRequireWildcard(_url); 14891 14892var _fn = _dereq_(83); 14893 14894var Fn = _interopRequireWildcard(_fn); 14895 14896var _log = _dereq_(86); 14897 14898var _log2 = _interopRequireDefault(_log); 14899 14900var _tsml = _dereq_(98); 14901 14902var _tsml2 = _interopRequireDefault(_tsml); 14903 14904var _browser = _dereq_(78); 14905 14906var browser = _interopRequireWildcard(_browser); 14907 14908var _document = _dereq_(94); 14909 14910var _document2 = _interopRequireDefault(_document); 14911 14912var _window = _dereq_(95); 14913 14914var _window2 = _interopRequireDefault(_window); 14915 14916var _obj = _dereq_(88); 14917 14918var _mergeOptions = _dereq_(87); 14919 14920var _mergeOptions2 = _interopRequireDefault(_mergeOptions); 14921 14922var _toTitleCase = _dereq_(91); 14923 14924var _toTitleCase2 = _interopRequireDefault(_toTitleCase); 14925 14926function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 14927 14928function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 14929 14930function _taggedTemplateLiteralLoose(strings, raw) { strings.raw = raw; return strings; } 14931 14932function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 14933 14934function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 14935 14936function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
vendor: 2,264 bytes, lines 14936-14971
14936onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 14937 * @file html5.js 14938 */ 14939 14940 14941/** 14942 * HTML5 Media Controller - Wrapper for HTML5 Media API 14943 * 14944 * @mixes Tech~SouceHandlerAdditions 14945 * @extends Tech 14946 */ 14947var Html5 = function (_Tech) { 14948 _inherits(Html5, _Tech); 14949 14950 /** 14951 * Create an instance of this Tech. 14952 * 14953 * @param {Object} [options] 14954 * The key/value store of player options. 14955 * 14956 * @param {Component~ReadyCallback} ready 14957 * Callback function to call when the `HTML5` Tech is ready. 14958 */ 14959 function Html5(options, ready) { 14960 _classCallCheck(this, Html5); 14961 14962 var _this = _possibleConstructorReturn(this, _Tech.call(this, options, ready)); 14963 14964 var source = options.source; 14965 var crossoriginTracks = false; 14966 14967 // Set the source if one is provided 14968 // 1) Check if the source is new (if not, we want to keep the original so playback isn't interrupted) 14969 // 2) Check to see if the network state of the tag was failed at init, and if so, reset the source 14970 // anyway so the error gets fired. 14971 if (source && (_this.el_.currentSrc !== source.src || options.tag && options.tag.
14971initNetworkState_ === 3)) { 14972 _this.setSource(source); 14973 } else { 14974 _this.handleLateInit_(_this.el_); 14975 } 14976 14977 if (_this.el_.hasChildNodes()) { 14978 14979 var nodes = _this.el_.childNodes; 14980 var nodesLength = nodes.length; 14981 var removeNodes = []; 14982 14983 while (nodesLength--) { 14984 var node = nodes[nodesLength]; 14985 var nodeName = node.nodeName.toLowerCase(); 14986 14987 if (nodeName === 'track') { 14988 if (!_this.featuresNativeTextTracks) { 14989 // Empty video tag tracks so the built-in player doesn't use them also. 14990 // This may not be fast enough to stop HTML5 browsers from reading the tags 14991 // so we'll need to turn off any default tracks if we're manually doing 14992 // captions and subtitles. videoElement.textTracks 14993 removeNodes.push(node); 14994 } else { 14995 // store HTMLTrackElement and TextTrack to remote list 14996 _this.remoteTextTrackEls().addTrackElement_(node); 14997 _this.remoteTextTracks().addTrack_(node.track); 14998 if (!crossoriginTracks && !_this.el_.hasAttribute('crossorigin') && Url.isCrossOrigin(node.src)) { 14999 crossoriginTracks = true; 15000 } 15001 } 15002 } 15003 } 15004 15005 for (var i = 0; i < removeNodes.length; i++) { 15006 _this.el_.removeChild(removeNodes[i]); 15007 } 15008 } 15009 15010 // TODO: add text tracks into this list 15011 var trackTypes = ['audio', 'video']; 15012 15013 // ProxyNative Video/Audio Track 15014 trackTypes.forEach(function (type) { 15015 var elTracks = _this.el()[type + 'Tracks']; 15016 var techTracks = _this[type + 'Tracks'](); 15017 var capitalType = (0, _toTitleCase2['default'])(type); 15018 15019 if (!_this['featuresNative' + capitalType + 'Tracks'] || !elTracks || !elTracks.addEventListener) { 15020 return; 15021 } 15022 15023 _this['handle' + capitalType + 'TrackChange_'] = function (e) { 15024 techTracks.trigger({ 15025 type: 'change', 15026 target: techTracks, 15027 currentTarget: techTracks, 15028 srcElement: techTracks 15029 }); 15030 }; 15031 15032 _this['handle' + capitalType + 'TrackAdd_'] = function (e) { 15033 return techTracks.addTrack(e.track); 15034 }; 15035 _this['handle' + capitalType + 'TrackRemove_'] = function (e) { 15036 return techTracks.removeTrack(e.track); 15037 }; 15038 15039 elTracks.addEventListener('change', _this['handle' + capitalType + 'TrackChange_']); 15040 elTracks.addEventListener('addtrack', _this['handle' + capitalType + 'TrackAdd_']); 15041 elTracks.addEventListener('removetrack', _this['handle' + capitalType + 'TrackRemove_']); 15042 _this['removeOld' + capitalType + 'Tracks_'] = function (e) { 15043 return _this.removeOldTracks_(techTracks, elTracks); 15044 }; 15045 15046 // Remove (native) tracks that are not used anymore 15047 _this.on('loadstart', _this['removeOld' + capitalType + 'Tracks_']); 15048 }); 15049 15050 if (_this.featuresNativeTextTracks) { 15051 if (crossoriginTracks) { 15052 _log2['default'].warn((0, _tsml2['default'])(_templateObject)); 15053 } 15054 15055 _this.handleTextTrackChange_ = Fn.bind(_this, _this.handleTextTrackChange); 15056 _this.handleTextTrackAdd_ = Fn.bind(_this, _this.handleTextTrackAdd); 15057 _this.handleTextTrackRemove_ = Fn.bind(_this, _this.handleTextTrackRemove); 15058 _this.proxyNativeTextTracks_(); 15059 } 15060 15061 // Determine if native controls should be used 15062 // Our goal should be to get the custom controls on mobile solid everywhere 15063 // so we can remove this all together. Right now this will block custom 15064 // controls on touch enabled laptops like the Chrome Pixel 15065 if ((browser.TOUCH_ENABLED || browser.IS_IPHONE || browser.IS_NATIVE_ANDROID) && options.nativeControlsForTouch === true) { 15066 _this.setControls(true); 15067 } 15068 15069 // on iOS, we want to proxy `webkitbeginfullscreen` and `webkitendfullscreen` 15070 // into a `fullscreenchange` event 15071 _this.proxyWebkitFullscreen_(); 15072 15073 _this.triggerReady(); 15074 return _this; 15075 } 15076 15077 /** 15078 * Dispose of `HTML5` media element and remove all tracks. 15079 */ 15080 15081 15082 Html5.prototype.dispose = function dispose() { 15083 var _this2 = this; 15084 15085 // Un-ProxyNativeTracks 15086 ['audio', 'video', 'text'].forEach(function (type) { 15087 var capitalType = (0, _toTitleCase2['default'])(type); 15088 var tl = _this2.el_[type + 'Tracks']; 15089 15090 if (tl && tl.removeEventListener) { 15091 tl.removeEventListener('change', _this2['handle' + capitalType + 'TrackChange_']); 15092 tl.removeEventListener('addtrack', _this2['handle' + capitalType + 'TrackAdd_']); 15093 tl.removeEventListener('removetrack', _this2['handle' + capitalType + 'TrackRemove_']); 15094 } 15095 15096 // Stop removing old text tracks 15097 if (tl) { 15098 _this2.off('loadstart', _this2['removeOld' + capitalType + 'Tracks_']); 15099 } 15100 }); 15101 15102 Html5.disposeMediaElement(this.el_); 15103 // tech will handle clearing of the emulated track list 15104 _Tech.prototype.dispose.call(this); 15105 }; 15106 15107 /** 15108 * Create the `Html5` Tech's DOM element. 15109 * 15110 * @return {Element} 15111 * The element that gets created. 15112 */ 15113 15114 15115 Html5.prototype.createEl = function createEl() { 15116 var el = this.options_.tag; 15117 15118 // Check if this browser supports moving the element into the box. 15119 // On the iPhone video will break if you move the element, 15120 // So we have to create a brand new element. 15121 // If we ingested the player div, we do not need to move the media element. 15122 if (!el || !(this.options_.playerElIngest || this.movingMediaElementInDOM)) { 15123 15124 // If the original tag is still there, clone and remove it. 15125 if (el) { 15126 var clone = el.cloneNode(true); 15127 15128 if (el.parentNode) { 15129 el.parentNode.insertBefore(clone, el); 15130 } 15131 Html5.disposeMediaElement(el); 15132 el = clone; 15133 } else { 15134 el = _document2['default'].createElement('video'); 15135 15136 // determine if native controls should be used 15137 var tagAttributes = this.options_.tag && Dom.getElAttributes(this.options_.tag); 15138 var attributes = (0, _mergeOptions2['default'])({}, tagAttributes); 15139 15140 if (!browser.TOUCH_ENABLED || this.options_.nativeControlsForTouch !== true) { 15141 delete attributes.controls; 15142 } 15143 15144 Dom.setElAttributes(el, (0, _obj.assign)(attributes, { 15145 id: this.options_.techId, 15146 'class': 'vjs-tech' 15147 })); 15148 } 15149 15150 el.playerId = this.options_.playerId; 15151 } 15152 15153 // Update specific tag settings, in case they were overridden 15154 var settingsAttrs = ['autoplay', 'preload', 'loop', 'muted', 'playsinline']; 15155 15156 for (var i = settingsAttrs.length - 1; i >= 0; i--) { 15157 var attr = settingsAttrs[i]; 15158 var overwriteAttrs = {}; 15159 15160 if (typeof this.options_[attr] !== 'undefined') { 15161 overwriteAttrs[attr] = this.options_[attr]; 15162 } 15163 Dom.setElAttributes(el, overwriteAttrs); 15164 } 15165 15166 return el; 15167 }; 15168 15169 /** 15170 * This will be triggered if the loadstart event has already fired, before videojs was 15171 * ready. Two known examples of when this can happen are: 15172 * 1. If we're loading the playback object after it has started loading 15173 * 2. The media is already playing the (often with autoplay on) then 15174 * 15175 * This function will fire another loadstart so that videojs can catchup. 15176 * 15177 * @fires Tech#loadstart 15178 * 15179 * @return {undefined} 15180 * returns nothing. 15181 */ 15182 15183 15184 Html5.prototype.handleLateInit_ = function handleLateInit_(el) { 15185 if (el.networkState === 0 || el.networkState === 3) { 15186 // The video element hasn't started loading the source yet 15187 // or didn't find a source 15188 return; 15189 } 15190 15191 if (el.readyState === 0) { 15192 // NetworkState is set synchronously BUT loadstart is fired at the 15193 // end of the current stack, usually before setInterval(fn, 0). 15194 // So at this point we know loadstart may have already fired or is 15195 // about to fire, and either way the player hasn't seen it yet. 15196 // We don't want to fire loadstart prematurely here and cause a 15197 // double loadstart so we'll wait and see if it happens between now 15198 // and the next loop, and fire it if not. 15199 // HOWEVER, we also want to make sure it fires before loadedmetadata 15200 // which could also happen between now and the next loop, so we'll 15201 // watch for that also. 15202 var loadstartFired = false;
vendor: 4,620 bytes, lines 15203-15365
15203 var setLoadstartFired = function setLoadstartFired() { 15204 loadstartFired = true; 15205 }; 15206 15207 this.on('loadstart', setLoadstartFired); 15208 15209 var triggerLoadstart = function triggerLoadstart() { 15210 // We did miss the original loadstart. Make sure the player 15211 // sees loadstart before loadedmetadata 15212 if (!loadstartFired) { 15213 this.trigger('loadstart'); 15214 } 15215 }; 15216 15217 this.on('loadedmetadata', triggerLoadstart); 15218 15219 this.ready(function () { 15220 this.off('loadstart', setLoadstartFired); 15221 this.off('loadedmetadata', triggerLoadstart); 15222 15223 if (!loadstartFired) { 15224 // We did miss the original native loadstart. Fire it now. 15225 this.trigger('loadstart'); 15226 } 15227 }); 15228 15229 return; 15230 } 15231 15232 // From here on we know that loadstart already fired and we missed it. 15233 // The other readyState events aren't as much of a problem if we double 15234 // them, so not going to go to as much trouble as loadstart to prevent 15235 // that unless we find reason to. 15236 var eventsToTrigger = ['loadstart']; 15237 15238 // loadedmetadata: newly equal to HAVE_METADATA (1) or greater 15239 eventsToTrigger.push('loadedmetadata'); 15240 15241 // loadeddata: newly increased to HAVE_CURRENT_DATA (2) or greater 15242 if (el.readyState >= 2) { 15243 eventsToTrigger.push('loadeddata'); 15244 } 15245 15246 // canplay: newly increased to HAVE_FUTURE_DATA (3) or greater 15247 if (el.readyState >= 3) { 15248 eventsToTrigger.push('canplay'); 15249 } 15250 15251 // canplaythrough: newly equal to HAVE_ENOUGH_DATA (4) 15252 if (el.readyState >= 4) { 15253 eventsToTrigger.push('canplaythrough'); 15254 } 15255 15256 // We still need to give the player time to add event listeners 15257 this.ready(function () { 15258 eventsToTrigger.forEach(function (type) { 15259 this.trigger(type); 15260 }, this); 15261 }); 15262 }; 15263 15264 /** 15265 * Add event listeners to native text track events. This adds the native text tracks 15266 * to our emulated {@link TextTrackList}. 15267 */ 15268 15269 15270 Html5.prototype.proxyNativeTextTracks_ = function proxyNativeTextTracks_() { 15271 var tt = this.el().textTracks; 15272 15273 if (tt) { 15274 // Add tracks - if player is initialised after DOM loaded, textTracks 15275 // will not trigger addtrack 15276 for (var i = 0; i < tt.length; i++) { 15277 this.textTracks().addTrack_(tt[i]); 15278 } 15279 15280 if (tt.addEventListener) { 15281 tt.addEventListener('change', this.handleTextTrackChange_); 15282 tt.addEventListener('addtrack', this.handleTextTrackAdd_); 15283 tt.addEventListener('removetrack', this.handleTextTrackRemove_); 15284 } 15285 15286 // Remove (native) texttracks that are not used anymore 15287 this.on('loadstart', this.removeOldTextTracks_); 15288 } 15289 }; 15290 15291 /** 15292 * Handle any {@link TextTrackList} `change` event. 15293 * 15294 * @param {EventTarget~Event} e 15295 * The `change` event that caused this to run. 15296 * 15297 * @listens TextTrackList#change 15298 */ 15299 15300 15301 Html5.prototype.handleTextTrackChange = function handleTextTrackChange(e) { 15302 var tt = this.textTracks(); 15303 15304 this.textTracks().trigger({ 15305 type: 'change', 15306 target: tt, 15307 currentTarget: tt, 15308 srcElement: tt 15309 }); 15310 }; 15311 15312 /** 15313 * Handle any {@link TextTrackList} `addtrack` event. 15314 * 15315 * @param {EventTarget~Event} e 15316 * The `addtrack` event that caused this to run. 15317 * 15318 * @listens TextTrackList#addtrack 15319 */ 15320 15321 15322 Html5.prototype.handleTextTrackAdd = function handleTextTrackAdd(e) { 15323 this.textTracks().addTrack_(e.track); 15324 }; 15325 15326 /** 15327 * Handle any {@link TextTrackList} `removetrack` event. 15328 * 15329 * @param {EventTarget~Event} e 15330 * The `removetrack` event that caused this to run. 15331 * 15332 * @listens TextTrackList#removetrack 15333 */ 15334 15335 15336 Html5.prototype.handleTextTrackRemove = function handleTextTrackRemove(e) { 15337 this.textTracks().removeTrack_(e.track); 15338 }; 15339 15340 /** 15341 * This function removes any {@link AudioTrack}s, {@link VideoTrack}s, or 15342 * {@link TextTrack}s that are not in the media elements TrackList. 15343 * 15344 * @param {TrackList} techTracks 15345 * HTML5 Tech's TrackList to search through 15346 * 15347 * @param {TrackList} elTracks 15348 * HTML5 media elements TrackList to search trough. 15349 * 15350 * @private 15351 */ 15352 15353 15354 Html5.prototype.removeOldTracks_ = function removeOldTracks_(techTracks, elTracks) { 15355 // This will loop over the techTracks and check if they are still used by the HTML5 media element 15356 // If not, they will be removed from the emulated list 15357 var removeTracks = []; 15358 15359 if (!elTracks) { 15360 return; 15361 } 15362 15363 for (var i = 0; i < techTracks.length; i++) { 15364 var techTrack = techTracks[i]; 15365 var found = false;
15366 15367 for (var j = 0; j < elTracks.length; j++) { 15368 if (elTracks[j] === techTrack) { 15369 found = true; 15370 break; 15371 } 15372 } 15373 15374 if (!found) { 15375 removeTracks.push(techTrack); 15376 } 15377 } 15378 15379 for (var _i = 0; _i < removeTracks.length; _i++) { 15380 var track = removeTracks[_i]; 15381 15382 techTracks.removeTrack_(track); 15383 } 15384 }; 15385 15386 /** 15387 * Remove {@link TextTrack}s that dont exist in the native track list from our 15388 * emulated {@link TextTrackList}. 15389 * 15390 * @listens Tech#loadstart 15391 */ 15392 15393 15394 Html5.prototype.removeOldTextTracks_ = function removeOldTextTracks_(e) { 15395 var techTracks = this.textTracks(); 15396 var elTracks = this.el().textTracks; 15397 15398 this.removeOldTracks_(techTracks, elTracks); 15399 }; 15400 15401 /** 15402 * Called by {@link Player#play} to play using the `Html5` `Tech`. 15403 */ 15404 15405 15406 Html5.prototype.play = function play() { 15407 var playPromise = this.el_.play(); 15408 15409 // Catch/silence error when a pause interrupts a play request 15410 // on browsers which return a promise 15411 if (playPromise !== undefined && typeof playPromise.then === 'function') { 15412 playPromise.then(null, function (e) {}); 15413 } 15414 }; 15415 15416 /** 15417 * Set current time for the `HTML5` tech. 15418 * 15419 * @param {number} seconds 15420 * Set the current time of the media to this. 15421 */ 15422 15423 15424 Html5.prototype.setCurrentTime = function setCurrentTime(seconds) { 15425 try { 15426 this.el_.currentTime = seconds; 15427 } catch (e) { 15428 (0, _log2['default'])(e, 'Video is not ready. (Video.js)'); 15429 // this.warning(VideoJS.warnings.videoNotReady); 15430 } 15431 }; 15432 15433 /** 15434 * Get the current duration of the HTML5 media element. 15435 * 15436 * @return {number} 15437 * The duration of the media or 0 if there is no duration. 15438 */ 15439 15440 15441 Html5.prototype.duration = function duration() { 15442 var _this3 = this; 15443 15444 // Android Chrome will report duration as Infinity for VOD HLS until after 15445 // playback has started, which triggers the live display erroneously. 15446 // Return NaN if playback has not started and trigger a durationupdate once 15447 // the duration can be reliably known. 15448 if (this.el_.duration === Infinity && browser.IS_ANDROID && browser.IS_CHROME) { 15449 if (this.el_.currentTime === 0) { 15450 // Wait for the first `timeupdate` with currentTime > 0 - there may be 15451 // several with 0 15452 var checkProgress = function checkProgress() { 15453 if (_this3.el_.currentTime > 0) { 15454 // Trigger durationchange for genuinely live video 15455 if (_this3.el_.duration === Infinity) { 15456 _this3.trigger('durationchange'); 15457 } 15458 _this3.off('timeupdate', checkProgress); 15459 } 15460 }; 15461 15462 this.on('timeupdate', checkProgress); 15463 return NaN; 15464 } 15465 } 15466 return this.el_.duration || NaN; 15467 }; 15468 15469 /** 15470 * Get the current width of the HTML5 media element. 15471 * 15472 * @return {number} 15473 * The width of the HTML5 media element. 15474 */ 15475 15476 15477 Html5.prototype.width = function width() { 15478 return this.el_.offsetWidth; 15479 }; 15480 15481 /** 15482 * Get the current height of the HTML5 media element. 15483 * 15484 * @return {number} 15485 * The heigth of the HTML5 media element. 15486 */ 15487 15488 15489 Html5.prototype.height = function height() { 15490 return this.el_.offsetHeight; 15491 }; 15492 15493 /** 15494 * Proxy iOS `webkitbeginfullscreen` and `webkitendfullscreen` into 15495 * `fullscreenchange` event. 15496 * 15497 * @private 15498 * @fires fullscreenchange 15499 * @listens webkitendfullscreen 15500 * @listens webkitbeginfullscreen 15501 * @listens webkitbeginfullscreen 15502 */ 15503 15504 15505 Html5.prototype.proxyWebkitFullscreen_ = function proxyWebkitFullscreen_() { 15506 var _this4 = this; 15507 15508 if (!('webkitDisplayingFullscreen' in this.el_)) { 15509 return; 15510 } 15511 15512 var endFn = function endFn() { 15513 this.trigger('fullscreenchange', { isFullscreen: false }); 15514 }; 15515 15516 var beginFn = function beginFn() { 15517 if ('webkitPresentationMode' in this.el_ && this.el_.webkitPresentationMode !== 'picture-in-picture') { 15518 this.one('webkitendfullscreen', endFn); 15519 15520 this.trigger('fullscreenchange', { isFullscreen: true }); 15521 } 15522 }; 15523 15524 this.on('webkitbeginfullscreen', beginFn); 15525 this.on('dispose', function () { 15526 _this4.off('webkitbeginfullscreen', beginFn); 15527 _this4.off('webkitendfullscreen', endFn); 15528 }); 15529 }; 15530 15531 /** 15532 * Check if fullscreen is supported on the current playback device. 15533 * 15534 * @return {boolean} 15535 * - True if fullscreen is supported. 15536 * - False if fullscreen is not supported. 15537 */ 15538 15539 15540 Html5.prototype.supportsFullScreen = function supportsFullScreen() { 15541 if (typeof this.el_.webkitEnterFullScreen === 'function') { 15542 var userAgent = _window2['default'].navigator && _window2['default'].navigator.userAgent || ''; 15543 15544 // Seems to be broken in Chromium/Chrome && Safari in Leopard 15545 if (/Android/.test(userAgent) || !/Chrome|Mac OS X 10.5/.test(userAgent)) { 15546 return true; 15547 } 15548 } 15549 return false;
15550 }; 15551 15552 /** 15553 * Request that the `HTML5` Tech enter fullscreen. 15554 */ 15555 15556 15557 Html5.prototype.enterFullScreen = function enterFullScreen() { 15558 var video = this.el_; 15559 15560 if (video.paused && video.networkState <= video.HAVE_METADATA) { 15561 // attempt to prime the video element for programmatic access 15562 // this isn't necessary on the desktop but shouldn't hurt 15563 this.el_.play(); 15564 15565 // playing and pausing synchronously during the transition to fullscreen 15566 // can get iOS ~6.1 devices into a play/pause loop 15567 this.setTimeout(function () { 15568 video.pause(); 15569 video.webkitEnterFullScreen(); 15570 }, 0); 15571 } else { 15572 video.webkitEnterFullScreen(); 15573 } 15574 }; 15575 15576 /** 15577 * Request that the `HTML5` Tech exit fullscreen. 15578 */ 15579 15580 15581 Html5.prototype.exitFullScreen = function exitFullScreen() { 15582 this.el_.webkitExitFullScreen(); 15583 }; 15584 15585 /** 15586 * A getter/setter for the `Html5` Tech's source object. 15587 * > Note: Please use {@link Html5#setSource} 15588 * 15589 * @param {Tech~SourceObject} [src] 15590 * The source object you want to set on the `HTML5` techs element. 15591 * 15592 * @return {Tech~SourceObject|undefined} 15593 * - The current source object when a source is not passed in. 15594 * - undefined when setting 15595 * 15596 * @deprecated Since version 5. 15597 */ 15598 15599 15600 Html5.prototype.src = function src(_src) { 15601 if (_src === undefined) { 15602 return this.el_.src; 15603 } 15604 15605 // Setting src through `src` instead of `setSrc` will be deprecated 15606 this.setSrc(_src); 15607 }; 15608 15609 /** 15610 * Reset the tech by removing all sources and then calling 15611 * {@link Html5.resetMediaElement}. 15612 */ 15613 15614 15615 Html5.prototype.reset = function reset() { 15616 Html5.resetMediaElement(this.el_); 15617 }; 15618 15619 /** 15620 * Get the current source on the `HTML5` Tech. Falls back to returning the source from 15621 * the HTML5 media element. 15622 * 15623 * @return {Tech~SourceObject} 15624 * The current source object from the HTML5 tech. With a fallback to the 15625 * elements source. 15626 */ 15627 15628 15629 Html5.prototype.currentSrc = function currentSrc() { 15630 if (this.currentSource_) { 15631 return this.currentSource_.src; 15632 } 15633 return this.el_.currentSrc; 15634 }; 15635 15636 /** 15637 * Set controls attribute for the HTML5 media Element. 15638 * 15639 * @param {string} val 15640 * Value to set the controls attribute to 15641 */ 15642 15643 15644 Html5.prototype.setControls = function setControls(val) { 15645 this.el_.controls = !!val; 15646 }; 15647 15648 /** 15649 * Create and returns a remote {@link TextTrack} object. 15650 * 15651 * @param {string} kind 15652 * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata) 15653 * 15654 * @param {string} [label] 15655 * Label to identify the text track 15656 * 15657 * @param {string} [language] 15658 * Two letter language abbreviation 15659 * 15660 * @return {TextTrack} 15661 * The TextTrack that gets created. 15662 */ 15663 15664 15665 Html5.prototype.addTextTrack = function addTextTrack(kind, label, language) { 15666 if (!this.featuresNativeTextTracks) { 15667 return _Tech.prototype.addTextTrack.call(this, kind, label, language); 15668 } 15669 15670 return this.el_.addTextTrack(kind, label, language); 15671 }; 15672 15673 /** 15674 * Creates either native TextTrack or an emulated TextTrack depending 15675 * on the value of `featuresNativeTextTracks` 15676 * 15677 * @param {Object} options 15678 * The object should contain the options to intialize the TextTrack with. 15679 * 15680 * @param {string} [options.kind] 15681 * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata). 15682 * 15683 * @param {string} [options.label]. 15684 * Label to identify the text track 15685 * 15686 * @param {string} [options.language] 15687 * Two letter language abbreviation. 15688 * 15689 * @param {boolean} [options.default] 15690 * Default this track to on. 15691 * 15692 * @param {string} [options.id] 15693 * The internal id to assign this track. 15694 * 15695 * @param {string} [options.src] 15696 * A source url for the track. 15697 * 15698 * @return {HTMLTrackElement} 15699 * The track element that gets created. 15700 */ 15701 15702 15703 Html5.prototype.createRemoteTextTrack = function createRemoteTextTrack(options) { 15704 if (!this.featuresNativeTextTracks) { 15705 return _Tech.prototype.createRemoteTextTrack.call(this, options); 15706 } 15707 var htmlTrackElement = _document2['default'].createElement('track'); 15708 15709 if (options.kind) { 15710 htmlTrackElement.kind = options.kind; 15711 } 15712 if (options.label) { 15713 htmlTrackElement.label = options.label; 15714 } 15715 if (options.language || options.srclang) { 15716 htmlTrackElement.srclang = options.language || options.srclang; 15717 } 15718 if (options['default']) { 15719 htmlTrackElement['default'] = options['default']; 15720 } 15721 if (options.id) { 15722 htmlTrackElement.id = options.id; 15723 } 15724 if (options.src) { 15725 htmlTrackElement.src = options.src; 15726 } 15727 15728 return htmlTrackElement; 15729 }; 15730 15731 /** 15732 * Creates a remote text track object and returns an html track element. 15733 * 15734 * @param {Object} options The object should contain values for 15735 * kind, language, label, and src (location of the WebVTT file) 15736 * @param {Boolean} [manualCleanup=true] if set to false, the TextTrack will be 15737 * automatically removed from the video element whenever the source changes 15738 * @return {HTMLTrackElement} An Html Track Element. 15739 * This can be an emulated {@link HTMLTrackElement} or a native one. 15740 * @deprecated The default value of the "manualCleanup" parameter will default 15741 * to "false" in upcoming versions of Video.js 15742 */ 15743 15744 15745 Html5.prototype.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) { 15746 var htmlTrackElement = _Tech.prototype.addRemoteTextTrack.call(this, options, manualCleanup); 15747 15748 if (this.featuresNativeTextTracks) { 15749 this.el().appendChild(htmlTrackElement); 15750 } 15751 15752 return htmlTrackElement; 15753 }; 15754 15755 /** 15756 * Remove remote `TextTrack` from `TextTrackList` object 15757 * 15758 * @param {TextTrack} track 15759 * `TextTrack` object to remove 15760 */ 15761 15762 15763 Html5.prototype.removeRemoteTextTrack = function removeRemoteTextTrack(track) { 15764 _Tech.prototype.removeRemoteTextTrack.call(this, track); 15765 15766 if (this.featuresNativeTextTracks) { 15767 var tracks = this.$$('track'); 15768 15769 var i = tracks.length; 15770 15771 while (i--) { 15772 if (track === tracks[i] || track === tracks[i].track) { 15773 this.el().removeChild(tracks[i]); 15774 } 15775 } 15776 } 15777 }; 15778 15779 /** 15780 * Get the value of `playsinline` from the media element. `playsinline` indicates 15781 * to the browser that non-fullscreen playback is preferred when fullscreen 15782 * playback is the native default, such as in iOS Safari. 15783 * 15784 * @method Html5#playsinline 15785 * @return {boolean} 15786 * - The value of `playsinline` from the media element. 15787 * - True indicates that the media should play inline. 15788 * - False indicates that the media should not play inline. 15789 * 15790 * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline} 15791 */ 15792 15793 15794 Html5.prototype.playsinline = function playsinline() { 15795 return this.el_.hasAttribute('playsinline'); 15796 }; 15797 15798 /** 15799 * Set the value of `playsinline` from the media element. `playsinline` indicates 15800 * to the browser that non-fullscreen playback is preferred when fullscreen 15801 * playback is the native default, such as in iOS Safari. 15802 * 15803 * @method Html5#setPlaysinline 15804 * @param {boolean} playsinline 15805 * - True indicates that the media should play inline. 15806 * - False indicates that the media should not play inline. 15807 * 15808 * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline} 15809 */ 15810 15811 15812 Html5.prototype.setPlaysinline = function setPlaysinline(value) { 15813 if (value) { 15814 this.el_.setAttribute('playsinline', 'playsinline'); 15815 } else { 15816 this.el_.removeAttribute('playsinline'); 15817 } 15818 }; 15819 15820 /** 15821 * Gets available media playback quality metrics as specified by the W3C's Media 15822 * Playback Quality API. 15823 * 15824 * @see [Spec]{@link https://wicg.github.io/media-playback-quality} 15825 * 15826 * @return {Object} 15827 * An object with supported media playback quality metrics 15828 */ 15829 15830 15831 Html5.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() { 15832 if (typeof this.el().getVideoPlaybackQuality === 'function') { 15833 return this.el().getVideoPlaybackQuality(); 15834 } 15835 15836 var videoPlaybackQuality = {}; 15837 15838 if (typeof this.el().webkitDroppedFrameCount !== 'undefined' && typeof this.el().webkitDecodedFrameCount !== 'undefined') { 15839 videoPlaybackQuality.droppedVideoFrames = this.el().webkitDroppedFrameCount; 15840 videoPlaybackQuality.totalVideoFrames = this.el().webkitDecodedFrameCount; 15841 } 15842 15843 if (_window2['default'].performance && typeof _window2['default'].performance.now === 'function') { 15844 videoPlaybackQuality.creationTime = _window2['default'].performance.now(); 15845 } else if (_window2['default'].performance && _window2['default'].performance.timing && typeof _window2['default'].performance.timing.navigationStart === 'number') { 15846 videoPlaybackQuality.creationTime = _window2['default'].Date.now() - _window2['default'].performance.timing.navigationStart; 15847 } 15848 15849 return videoPlaybackQuality; 15850 }; 15851 15852 return Html5; 15853}(_tech2['default']); 15854 15855/* HTML5 Support Testing ---------------------------------------------------- */ 15856
15857if (Dom.isReal()) { 15858 15859 /** 15860 * Element for testing browser HTML5 media capabilities 15861 * 15862 * @type {Element} 15863 * @constant 15864 * @private 15865 */ 15866 Html5.TEST_VID = _document2['default'].createElement('video'); 15867 var track = _document2['default'].createElement('track'); 15868 15869 track.kind = 'captions'; 15870 track.srclang = 'en'; 15871 track.label = 'English'; 15872 Html5.TEST_VID.appendChild(track); 15873} 15874 15875/** 15876 * Check if HTML5 media is supported by this browser/device. 15877 * 15878 * @return {boolean} 15879 * - True if HTML5 media is supported. 15880 * - False if HTML5 media is not supported. 15881 */ 15882Html5.isSupported = function () { 15883 // IE9 with no Media Player is a LIAR! (#984) 15884 try { 15885 Html5.TEST_VID.volume = 0.5; 15886 } catch (e) { 15887 return false; 15888 } 15889 15890 return !!(Html5.TEST_VID && Html5.TEST_VID.canPlayType); 15891}; 15892 15893/** 15894 * Check if the volume can be changed in this browser/device. 15895 * Volume cannot be changed in a lot of mobile devices. 15896 * Specifically, it can't be changed from 1 on iOS. 15897 * 15898 * @return {boolean} 15899 * - True if volume can be controlled 15900 * - False otherwise 15901 */ 15902Html5.canControlVolume = function () { 15903 // IE will error if Windows Media Player not installed #3315 15904 try { 15905 var volume = Html5.TEST_VID.volume; 15906 15907 Html5.TEST_VID.volume = volume / 2 + 0.1; 15908 return volume !== Html5.TEST_VID.volume; 15909 } catch (e) { 15910 return false; 15911 } 15912}; 15913 15914/** 15915 * Check if the playback rate can be changed in this browser/device. 15916 * 15917 * @return {boolean} 15918 * - True if playback rate can be controlled 15919 * - False otherwise 15920 */ 15921Html5.canControlPlaybackRate = function () { 15922 // Playback rate API is implemented in Android Chrome, but doesn't do anything 15923 // https://github.com/videojs/video.js/issues/3180 15924 if (browser.IS_ANDROID && browser.IS_CHROME && browser.CHROME_VERSION < 58) { 15925 return false; 15926 } 15927 // IE will error if Windows Media Player not installed #3315 15928 try { 15929 var playbackRate = Html5.TEST_VID.playbackRate; 15930 15931 Html5.TEST_VID.playbackRate = playbackRate / 2 + 0.1; 15932 return playbackRate !== Html5.TEST_VID.playbackRate; 15933 } catch (e) { 15934 return false; 15935 } 15936}; 15937 15938/** 15939 * Check to see if native `TextTrack`s are supported by this browser/device. 15940 * 15941 * @return {boolean} 15942 * - True if native `TextTrack`s are supported. 15943 * - False otherwise 15944 */ 15945Html5.supportsNativeTextTracks = function () { 15946 return browser.IS_ANY_SAFARI; 15947}; 15948 15949/** 15950 * Check to see if native `VideoTrack`s are supported by this browser/device 15951 * 15952 * @return {boolean} 15953 * - True if native `VideoTrack`s are supported. 15954 * - False otherwise 15955 */ 15956Html5.supportsNativeVideoTracks = function () { 15957 return !!(Html5.TEST_VID && Html5.TEST_VID.videoTracks); 15958}; 15959 15960/** 15961 * Check to see if native `AudioTrack`s are supported by this browser/device 15962 * 15963 * @return {boolean} 15964 * - True if native `AudioTrack`s are supported. 15965 * - False otherwise 15966 */ 15967Html5.supportsNativeAudioTracks = function () { 15968 return !!(Html5.TEST_VID && Html5.TEST_VID.audioTracks); 15969}; 15970 15971/** 15972 * An array of events available on the Html5 tech. 15973 * 15974 * @private 15975 * @type {Array} 15976 */ 15977Html5.Events = ['loadstart', 'suspend', 'abort', 'error', 'emptied', 'stalled', 'loadedmetadata', 'loadeddata', 'canplay', 'canplaythrough', 'playing', 'waiting', 'seeking', 'seeked', 'ended', 'durationchange', 'timeupdate', 'progress', 'play', 'pause', 'ratechange', 'volumechange']; 15978 15979/** 15980 * Boolean indicating whether the `Tech` supports volume control. 15981 * 15982 * @type {boolean} 15983 * @default {@link Html5.canControlVolume} 15984 */ 15985Html5.prototype.featuresVolumeControl = Html5.canControlVolume(); 15986 15987/** 15988 * Boolean indicating whether the `Tech` supports changing the speed at which the media 15989 * plays. Examples: 15990 * - Set player to play 2x (twice) as fast 15991 * - Set player to play 0.5x (half) as fast 15992 * 15993 * @type {boolean} 15994 * @default {@link Html5.canControlPlaybackRate} 15995 */ 15996Html5.prototype.featuresPlaybackRate = Html5.canControlPlaybackRate(); 15997 15998/** 15999 * Boolean indicating whether the `HTML5` tech currently supports the media element 16000 * moving in the DOM. iOS breaks if you move the media element, so this is set this to 16001 * false there. Everywhere else this should be true. 16002 * 16003 * @type {boolean} 16004 * @default 16005 */ 16006Html5.prototype.movingMediaElementInDOM = !browser.IS_IOS; 16007 16008// TODO: Previous comment: No longer appears to be used. Can probably be removed. 16009// Is this true? 16010/** 16011 * Boolean indicating whether the `HTML5` tech currently supports automatic media resize 16012 * when going into fullscreen. 16013 * 16014 * @type {boolean} 16015 * @default 16016 */ 16017Html5.prototype.featuresFullscreenResize = true; 16018 16019/** 16020 * Boolean indicating whether the `HTML5` tech currently supports the progress event. 16021 * If this is false, manual `progress` events will be triggred instead. 16022 * 16023 * @type {boolean} 16024 * @default 16025 */ 16026Html5.prototype.featuresProgressEvents = true; 16027 16028/** 16029 * Boolean indicating whether the `HTML5` tech currently supports the timeupdate event. 16030 * If this is false, manual `timeupdate` events will be triggred instead. 16031 * 16032 * @default 16033 */ 16034Html5.prototype.featuresTimeupdateEvents = true; 16035 16036/** 16037 * Boolean indicating whether the `HTML5` tech currently supports native `TextTrack`s. 16038 * 16039 * @type {boolean} 16040 * @default {@link Html5.supportsNativeTextTracks} 16041 */ 16042Html5.prototype.featuresNativeTextTracks = Html5.supportsNativeTextTracks(); 16043 16044/** 16045 * Boolean indicating whether the `HTML5` tech currently supports native `VideoTrack`s. 16046 * 16047 * @type {boolean} 16048 * @default {@link Html5.supportsNativeVideoTracks} 16049 */ 16050Html5.prototype.featuresNativeVideoTracks = Html5.supportsNativeVideoTracks(); 16051 16052/** 16053 * Boolean indicating whether the `HTML5` tech currently supports native `AudioTrack`s. 16054 * 16055 * @type {boolean} 16056 * @default {@link Html5.supportsNativeAudioTracks} 16057 */ 16058Html5.prototype.featuresNativeAudioTracks = Html5.supportsNativeAudioTracks(); 16059 16060// HTML5 Feature detection and Device Fixes --------------------------------- //
vendor: 10,190 bytes, lines 16061-16373
16061var canPlayType = Html5.TEST_VID && Html5.TEST_VID.constructor.prototype.canPlayType; 16062var mpegurlRE = /^application\/(?:x-|vnd\.apple\.)mpegurl/i; 16063var mp4RE = /^video\/mp4/i; 16064 16065Html5.patchCanPlayType = function () { 16066 16067 // Android 4.0 and above can play HLS to some extent but it reports being unable to do so 16068 if (browser.ANDROID_VERSION >= 4.0 && !browser.IS_FIREFOX) { 16069 Html5.TEST_VID.constructor.prototype.canPlayType = function (type) { 16070 if (type && mpegurlRE.test(type)) { 16071 return 'maybe'; 16072 } 16073 return canPlayType.call(this, type); 16074 }; 16075 16076 // Override Android 2.2 and less canPlayType method which is broken 16077 } else if (browser.IS_OLD_ANDROID) { 16078 Html5.TEST_VID.constructor.prototype.canPlayType = function (type) { 16079 if (type && mp4RE.test(type)) { 16080 return 'maybe'; 16081 } 16082 return canPlayType.call(this, type); 16083 }; 16084 } 16085}; 16086 16087Html5.unpatchCanPlayType = function () { 16088 var r = Html5.TEST_VID.constructor.prototype.canPlayType; 16089 16090 Html5.TEST_VID.constructor.prototype.canPlayType = canPlayType; 16091 return r; 16092}; 16093 16094// by default, patch the media element 16095Html5.patchCanPlayType(); 16096 16097Html5.disposeMediaElement = function (el) { 16098 if (!el) { 16099 return; 16100 } 16101 16102 if (el.parentNode) { 16103 el.parentNode.removeChild(el); 16104 } 16105 16106 // remove any child track or source nodes to prevent their loading 16107 while (el.hasChildNodes()) { 16108 el.removeChild(el.firstChild); 16109 } 16110 16111 // remove any src reference. not setting `src=''` because that causes a warning 16112 // in firefox 16113 el.removeAttribute('src'); 16114 16115 // force the media element to update its loading state by calling load() 16116 // however IE on Windows 7N has a bug that throws an error so need a try/catch (#793) 16117 if (typeof el.load === 'function') { 16118 // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473) 16119 (function () { 16120 try { 16121 el.load(); 16122 } catch (e) { 16123 // not supported 16124 } 16125 })(); 16126 } 16127}; 16128 16129Html5.resetMediaElement = function (el) { 16130 if (!el) { 16131 return; 16132 } 16133 16134 var sources = el.querySelectorAll('source'); 16135 var i = sources.length; 16136 16137 while (i--) { 16138 el.removeChild(sources[i]); 16139 } 16140 16141 // remove any src reference. 16142 // not setting `src=''` because that throws an error 16143 el.removeAttribute('src'); 16144 16145 if (typeof el.load === 'function') { 16146 // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473) 16147 (function () { 16148 try { 16149 el.load(); 16150 } catch (e) { 16151 // satisfy linter 16152 } 16153 })(); 16154 } 16155}; 16156 16157/* Native HTML5 element property wrapping ----------------------------------- */ 16158// Wrap native properties with a getter 16159[ 16160/** 16161 * Get the value of `paused` from the media element. `paused` indicates whether the media element 16162 * is currently paused or not. 16163 * 16164 * @method Html5#paused 16165 * @return {boolean} 16166 * The value of `paused` from the media element. 16167 * 16168 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-paused} 16169 */ 16170'paused', 16171 16172/** 16173 * Get the value of `currentTime` from the media element. `currentTime` indicates 16174 * the current second that the media is at in playback. 16175 * 16176 * @method Html5#currentTime 16177 * @return {number} 16178 * The value of `currentTime` from the media element. 16179 * 16180 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-currenttime} 16181 */ 16182'currentTime', 16183 16184/** 16185 * Get the value of `buffered` from the media element. `buffered` is a `TimeRange` 16186 * object that represents the parts of the media that are already downloaded and 16187 * available for playback. 16188 * 16189 * @method Html5#buffered 16190 * @return {TimeRange} 16191 * The value of `buffered` from the media element. 16192 * 16193 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-buffered} 16194 */ 16195'buffered', 16196 16197/** 16198 * Get the value of `volume` from the media element. `volume` indicates 16199 * the current playback volume of audio for a media. `volume` will be a value from 0 16200 * (silent) to 1 (loudest and default). 16201 * 16202 * @method Html5#volume 16203 * @return {number} 16204 * The value of `volume` from the media element. Value will be between 0-1. 16205 * 16206 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume} 16207 */ 16208'volume', 16209 16210/** 16211 * Get the value of `muted` from the media element. `muted` indicates 16212 * that the volume for the media should be set to silent. This does not actually change 16213 * the `volume` attribute. 16214 * 16215 * @method Html5#muted 16216 * @return {boolean} 16217 * - True if the value of `volume` should be ignored and the audio set to silent. 16218 * - False if the value of `volume` should be used. 16219 * 16220 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted} 16221 */ 16222'muted', 16223 16224/** 16225 * Get the value of `poster` from the media element. `poster` indicates 16226 * that the url of an image file that can/will be shown when no media data is available. 16227 * 16228 * @method Html5#poster 16229 * @return {string} 16230 * The value of `poster` from the media element. Value will be a url to an 16231 * image. 16232 * 16233 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-video-poster} 16234 */ 16235'poster', 16236 16237/** 16238 * Get the value of `preload` from the media element. `preload` indicates 16239 * what should download before the media is interacted with. It can have the following 16240 * values: 16241 * - none: nothing should be downloaded 16242 * - metadata: poster and the first few frames of the media may be downloaded to get 16243 * media dimensions and other metadata 16244 * - auto: allow the media and metadata for the media to be downloaded before 16245 * interaction 16246 * 16247 * @method Html5#preload 16248 * @return {string} 16249 * The value of `preload` from the media element. Will be 'none', 'metadata', 16250 * or 'auto'. 16251 * 16252 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload} 16253 */ 16254'preload', 16255 16256/** 16257 * Get the value of `autoplay` from the media element. `autoplay` indicates 16258 * that the media should start to play as soon as the page is ready. 16259 * 16260 * @method Html5#autoplay 16261 * @return {boolean} 16262 * - The value of `autoplay` from the media element. 16263 * - True indicates that the media should start as soon as the page loads. 16264 * - False indicates that the media should not start as soon as the page loads. 16265 * 16266 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay} 16267 */ 16268'autoplay', 16269 16270/** 16271 * Get the value of `controls` from the media element. `controls` indicates 16272 * whether the native media controls should be shown or hidden. 16273 * 16274 * @method Html5#controls 16275 * @return {boolean} 16276 * - The value of `controls` from the media element. 16277 * - True indicates that native controls should be showing. 16278 * - False indicates that native controls should be hidden. 16279 * 16280 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-controls} 16281 */ 16282'controls', 16283 16284/** 16285 * Get the value of `loop` from the media element. `loop` indicates 16286 * that the media should return to the start of the media and continue playing once 16287 * it reaches the end. 16288 * 16289 * @method Html5#loop 16290 * @return {boolean} 16291 * - The value of `loop` from the media element. 16292 * - True indicates that playback should seek back to start once 16293 * the end of a media is reached. 16294 * - False indicates that playback should not loop back to the start when the 16295 * end of the media is reached. 16296 * 16297 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop} 16298 */ 16299'loop', 16300 16301/** 16302 * Get the value of the `error` from the media element. `error` indicates any 16303 * MediaError that may have occured during playback. If error returns null there is no 16304 * current error. 16305 * 16306 * @method Html5#error 16307 * @return {MediaError|null} 16308 * The value of `error` from the media element. Will be `MediaError` if there 16309 * is a current error and null otherwise. 16310 * 16311 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-error} 16312 */ 16313'error', 16314 16315/** 16316 * Get the value of `seeking` from the media element. `seeking` indicates whether the 16317 * media is currently seeking to a new position or not. 16318 * 16319 * @method Html5#seeking 16320 * @return {boolean} 16321 * - The value of `seeking` from the media element. 16322 * - True indicates that the media is currently seeking to a new position. 16323 * - Flase indicates that the media is not seeking to a new position at this time. 16324 * 16325 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seeking} 16326 */ 16327'seeking', 16328 16329/** 16330 * Get the value of `seekable` from the media element. `seekable` returns a 16331 * `TimeRange` object indicating ranges of time that can currently be `seeked` to. 16332 * 16333 * @method Html5#seekable 16334 * @return {TimeRange} 16335 * The value of `seekable` from the media element. A `TimeRange` object 16336 * indicating the current ranges of time that can be seeked to. 16337 * 16338 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seekable} 16339 */ 16340'seekable', 16341 16342/** 16343 * Get the value of `ended` from the media element. `ended` indicates whether 16344 * the media has reached the end or not. 16345 * 16346 * @method Html5#ended 16347 * @return {boolean} 16348 * - The value of `ended` from the media element. 16349 * - True indicates that the media has ended. 16350 * - False indicates that the media has not ended. 16351 * 16352 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-ended} 16353 */ 16354'ended', 16355 16356/** 16357 * Get the value of `defaultMuted` from the media element. `defaultMuted` indicates 16358 * whether the media should start muted or not. Only changes the default state of the 16359 * media. `muted` and `defaultMuted` can have different values. `muted` indicates the 16360 * current state. 16361 * 16362 * @method Html5#defaultMuted 16363 * @return {boolean} 16364 * - The value of `defaultMuted` from the media element. 16365 * - True indicates that the media should start muted. 16366 * - False indicates that the media should not start muted 16367 * 16368 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted} 16369 */ 16370'defaultMuted', 16371 16372/** 16373 * Get the value of `playbackRate` from the media element. `playbackRate` indicates
16374 * the rate at which the media is currently playing back. Examples: 16375 * - if playbackRate is set to 2, media will play twice as fast. 16376 * - if playbackRate is set to 0.5, media will play half as fast. 16377 * 16378 * @method Html5#playbackRate 16379 * @return {number} 16380 * The value of `playbackRate` from the media element. A number indicating 16381 * the current playback speed of the media, where 1 is normal speed. 16382 * 16383 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate} 16384 */ 16385'playbackRate', 16386 16387/** 16388 * Get the value of `played` from the media element. `played` returns a `TimeRange` 16389 * object representing points in the media timeline that have been played. 16390 * 16391 * @method Html5#played 16392 * @return {TimeRange} 16393 * The value of `played` from the media element. A `TimeRange` object indicating 16394 * the ranges of time that have been played. 16395 * 16396 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-played} 16397 */ 16398'played', 16399 16400/** 16401 * Get the value of `networkState` from the media element. `networkState` indicates 16402 * the current network state. It returns an enumeration from the following list: 16403 * - 0: NETWORK_EMPTY 16404 * - 1: NEWORK_IDLE 16405 * - 2: NETWORK_LOADING 16406 * - 3: NETWORK_NO_SOURCE 16407 * 16408 * @method Html5#networkState 16409 * @return {number} 16410 * The value of `networkState` from the media element. This will be a number 16411 * from the list in the description. 16412 * 16413 * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-networkstate} 16414 */ 16415'networkState', 16416 16417/** 16418 * Get the value of `readyState` from the media element. `readyState` indicates 16419 * the current state of the media element. It returns an enumeration from the 16420 * following list: 16421 * - 0: HAVE_NOTHING 16422 * - 1: HAVE_METADATA 16423 * - 2: HAVE_CURRENT_DATA 16424 * - 3: HAVE_FUTURE_DATA 16425 * - 4: HAVE_ENOUGH_DATA 16426 * 16427 * @method Html5#readyState 16428 * @return {number} 16429 * The value of `readyState` from the media element. This will be a number 16430 * from the list in the description. 16431 * 16432 * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#ready-states} 16433 */ 16434'readyState', 16435 16436/** 16437 * Get the value of `videoWidth` from the video element. `videoWidth` indicates 16438 * the current width of the video in css pixels. 16439 * 16440 * @method Html5#videoWidth 16441 * @return {number} 16442 * The value of `videoWidth` from the video element. This will be a number 16443 * in css pixels. 16444 * 16445 * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth} 16446 */ 16447'videoWidth', 16448 16449/** 16450 * Get the value of `videoHeight` from the video element. `videoHeigth` indicates 16451 * the current height of the video in css pixels. 16452 * 16453 * @method Html5#videoHeight 16454 * @return {number} 16455 * The value of `videoHeight` from the video element. This will be a number 16456 * in css pixels. 16457 * 16458 * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth} 16459 */ 16460'videoHeight'].forEach(function (prop) { 16461 Html5.prototype[prop] = function () { 16462 return this.el_[prop]; 16463 }; 16464}); 16465 16466// Wrap native properties with a setter in this format: 16467// set + toTitleCase(name) 16468[ 16469/** 16470 * Set the value of `volume` on the media element. `volume` indicates the current 16471 * audio level as a percentage in decimal form. This means that 1 is 100%, 0.5 is 50%, and 16472 * so on. 16473 * 16474 * @method Html5#setVolume 16475 * @param {number} percentAsDecimal 16476 * The volume percent as a decimal. Valid range is from 0-1. 16477 * 16478 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume} 16479 */ 16480'volume', 16481 16482/** 16483 * Set the value of `muted` on the media element. `muted` indicates the current 16484 * audio level should be silent. 16485 * 16486 * @method Html5#setMuted 16487 * @param {boolean} muted 16488 * - True if the audio should be set to silent 16489 * - False otherwise 16490 * 16491 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted} 16492 */ 16493'muted', 16494 16495/** 16496 * Set the value of `src` on the media element. `src` indicates the current 16497 * {@link Tech~SourceObject} for the media. 16498 * 16499 * @method Html5#setSrc 16500 * @param {Tech~SourceObject} src 16501 * The source object to set as the current source. 16502 * 16503 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-src} 16504 */ 16505'src', 16506 16507/** 16508 * Set the value of `poster` on the media element. `poster` is the url to 16509 * an image file that can/will be shown when no media data is available. 16510 * 16511 * @method Html5#setPoster 16512 * @param {string} poster 16513 * The url to an image that should be used as the `poster` for the media 16514 * element. 16515 * 16516 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-poster} 16517 */ 16518'poster', 16519 16520/** 16521 * Set the value of `preload` on the media element. `preload` indicates 16522 * what should download before the media is interacted with. It can have the following 16523 * values: 16524 * - none: nothing should be downloaded 16525 * - metadata: poster and the first few frames of the media may be downloaded to get 16526 * media dimensions and other metadata 16527 * - auto: allow the media and metadata for the media to be downloaded before 16528 * interaction 16529 * 16530 * @method Html5#setPreload 16531 * @param {string} preload 16532 * The value of `preload` to set on the media element. Must be 'none', 'metadata', 16533 * or 'auto'. 16534 * 16535 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload} 16536 */ 16537'preload', 16538 16539/** 16540 * Set the value of `autoplay` on the media element. `autoplay` indicates 16541 * that the media should start to play as soon as the page is ready. 16542 * 16543 * @method Html5#setAutoplay 16544 * @param {boolean} autoplay 16545 * - True indicates that the media should start as soon as the page loads. 16546 * - False indicates that the media should not start as soon as the page loads. 16547 * 16548 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay} 16549 */ 16550'autoplay', 16551 16552/** 16553 * Set the value of `loop` on the media element. `loop` indicates 16554 * that the media should return to the start of the media and continue playing once 16555 * it reaches the end. 16556 * 16557 * @method Html5#setLoop 16558 * @param {boolean} loop 16559 * - True indicates that playback should seek back to start once 16560 * the end of a media is reached. 16561 * - False indicates that playback should not loop back to the start when the 16562 * end of the media is reached. 16563 * 16564 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop} 16565 */ 16566'loop', 16567 16568/** 16569 * Set the value of `playbackRate` on the media element. `playbackRate` indicates 16570 * the rate at which the media should play back. Examples: 16571 * - if playbackRate is set to 2, media will play twice as fast. 16572 * - if playbackRate is set to 0.5, media will play half as fast. 16573 * 16574 * @method Html5#setPlaybackRate 16575 * @return {number} 16576 * The value of `playbackRate` from the media element. A number indicating 16577 * the current playback speed of the media, where 1 is normal speed. 16578 * 16579 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate} 16580 */ 16581'playbackRate'].forEach(function (prop) { 16582 Html5.prototype['set' + (0, _toTitleCase2['default'])(prop)] = function (v) { 16583 this.el_[prop] = v; 16584 }; 16585}); 16586 16587// wrap native functions with a function 16588[ 16589/** 16590 * A wrapper around the media elements `pause` function. This will call the `HTML5` 16591 * media elements `pause` function. 16592 * 16593 * @method Html5#pause 16594 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-pause} 16595 */ 16596'pause', 16597 16598/** 16599 * A wrapper around the media elements `load` function. This will call the `HTML5`s 16600 * media element `load` function. 16601 * 16602 * @method Html5#load 16603 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-load} 16604 */ 16605'load'].forEach(function (prop) { 16606 Html5.prototype[prop] = function () { 16607 return this.el_[prop](); 16608 }; 16609}); 16610 16611_tech2['default'].withSourceHandlers(Html5); 16612 16613/** 16614 * Native source handler for Html5, simply passes the source to the media element. 16615 * 16616 * @proprety {Tech~SourceObject} source 16617 * The source object 16618 * 16619 * @proprety {Html5} tech 16620 * The instance of the HTML5 tech. 16621 */ 16622Html5.nativeSourceHandler = {}; 16623 16624/** 16625 * Check if the media element can play the given mime type. 16626 * 16627 * @param {string} type 16628 * The mimetype to check 16629 * 16630 * @return {string} 16631 * 'probably', 'maybe', or '' (empty string) 16632 */ 16633Html5.nativeSourceHandler.canPlayType = function (type) { 16634 // IE9 on Windows 7 without MediaPlayer throws an error here 16635 // https://github.com/videojs/video.js/issues/519 16636 try {
16637 return Html5.TEST_VID.canPlayType(type); 16638 } catch (e) { 16639 return ''; 16640 } 16641}; 16642 16643/** 16644 * Check if the media element can handle a source natively. 16645 * 16646 * @param {Tech~SourceObject} source 16647 * The source object 16648 * 16649 * @param {Object} [options] 16650 * Options to be passed to the tech. 16651 * 16652 * @return {string} 16653 * 'probably', 'maybe', or '' (empty string). 16654 */ 16655Html5.nativeSourceHandler.canHandleSource = function (source, options) { 16656 16657 // If a type was provided we should rely on that 16658 if (source.type) { 16659 return Html5.nativeSourceHandler.canPlayType(source.type); 16660 16661 // If no type, fall back to checking 'video/[EXTENSION]' 16662 } else if (source.src) { 16663 var ext = Url.getFileExtension(source.src); 16664 16665 return Html5.nativeSourceHandler.canPlayType('video/' + ext); 16666 } 16667 16668 return ''; 16669}; 16670 16671/** 16672 * Pass the source to the native media element. 16673 * 16674 * @param {Tech~SourceObject} source 16675 * The source object 16676 * 16677 * @param {Html5} tech 16678 * The instance of the Html5 tech 16679 * 16680 * @param {Object} [options] 16681 * The options to pass to the source 16682 */ 16683Html5.nativeSourceHandler.handleSource = function (source, tech, options) { 16684 tech.setSrc(source.src); 16685}; 16686 16687/** 16688 * A noop for the native dispose function, as cleanup is not needed. 16689 */ 16690Html5.nativeSourceHandler.dispose = function () {}; 16691 16692// Register the native source handler 16693Html5.registerSourceHandler(Html5.nativeSourceHandler); 16694 16695_component2['default'].registerComponent('Html5', Html5); 16696_tech2['default'].registerTech('Html5', Html5); 16697exports['default'] = Html5; 16698 16699},{"5":5,"62":62,"78":78,"81":81,"83":83,"86":86,"87":87,"88":88,"91":91,"92":92,"94":94,"95":95,"98":98}],61:[function(_dereq_,module,exports){ 16700'use strict'; 16701 16702exports.__esModule = true; 16703 16704var _component = _dereq_(5); 16705 16706var _component2 = _interopRequireDefault(_component); 16707 16708var _tech = _dereq_(62); 16709 16710var _tech2 = _interopRequireDefault(_tech); 16711 16712var _toTitleCase = _dereq_(91); 16713 16714var _toTitleCase2 = _interopRequireDefault(_toTitleCase); 16715 16716function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 16717 16718function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 16719 16720function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 16721 16722function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 16723 * @file loader.js 16724 */ 16725 16726 16727/** 16728 * The `MediaLoader` is the `Component` that decides which playback technology to load 16729 * when a player is initialized. 16730 * 16731 * @extends Component 16732 */ 16733var MediaLoader = function (_Component) { 16734 _inherits(MediaLoader, _Component); 16735 16736 /** 16737 * Create an instance of this class. 16738 * 16739 * @param {Player} player 16740 * The `Player` that this class should attach to. 16741 * 16742 * @param {Object} [options] 16743 * The key/value stroe of player options. 16744 * 16745 * @param {Component~ReadyCallback} [ready] 16746 * The function that is run when this component is ready. 16747 */ 16748 function MediaLoader(player, options, ready) { 16749 _classCallCheck(this, MediaLoader); 16750 16751 // If there are no sources when the player is initialized, 16752 // load the first supported playback technology. 16753 16754 var _this = _possibleConstructorReturn(this, _Component.call(this, player, options, ready)); 16755 16756 if (!options.playerOptions.sources || options.playerOptions.sources.length === 0) { 16757 for (var i = 0, j = options.playerOptions.techOrder; i < j.length; i++) { 16758 var techName = (0, _toTitleCase2['default'])(j[i]); 16759 var tech = _tech2['default'].getTech(techName); 16760
16761 // Support old behavior of techs being registered as components. 16762 // Remove once that deprecated behavior is removed. 16763 if (!techName) { 16764 tech = _component2['default'].getComponent(techName); 16765 } 16766 16767 // Check if the browser supports this technology 16768 if (tech && tech.isSupported()) { 16769 player.loadTech_(techName); 16770 break; 16771 } 16772 } 16773 } else { 16774 // Loop through playback technologies (HTML5, Flash) and check for support. 16775 // Then load the best source. 16776 // A few assumptions here: 16777 // All playback technologies respect preload false. 16778 player.src(options.playerOptions.sources); 16779 } 16780 return _this; 16781 } 16782 16783 return MediaLoader; 16784}(_component2['default']); 16785 16786_component2['default'].registerComponent('MediaLoader', MediaLoader); 16787exports['default'] = MediaLoader; 16788 16789},{"5":5,"62":62,"91":91}],62:[function(_dereq_,module,exports){ 16790'use strict'; 16791 16792exports.__esModule = true; 16793 16794var _component = _dereq_(5); 16795 16796var _component2 = _interopRequireDefault(_component); 16797 16798var _htmlTrackElement = _dereq_(66); 16799 16800var _htmlTrackElement2 = _interopRequireDefault(_htmlTrackElement); 16801
16802var _htmlTrackElementList = _dereq_(65); 16803 16804var _htmlTrackElementList2 = _interopRequireDefault(_htmlTrackElementList); 16805 16806var _mergeOptions = _dereq_(87); 16807 16808var _mergeOptions2 = _interopRequireDefault(_mergeOptions); 16809 16810var _textTrack = _dereq_(72); 16811 16812var _textTrack2 = _interopRequireDefault(_textTrack); 16813 16814var _textTrackList = _dereq_(70); 16815 16816var _textTrackList2 = _interopRequireDefault(_textTrackList); 16817 16818var _videoTrackList = _dereq_(76); 16819 16820var _videoTrackList2 = _interopRequireDefault(_videoTrackList); 16821 16822var _audioTrackList = _dereq_(63); 16823 16824var _audioTrackList2 = _interopRequireDefault(_audioTrackList); 16825 16826var _fn = _dereq_(83); 16827 16828var Fn = _interopRequireWildcard(_fn); 16829 16830var _log = _dereq_(86); 16831 16832var _log2 = _interopRequireDefault(_log); 16833 16834var _timeRanges = _dereq_(90); 16835 16836var _buffer = _dereq_(79); 16837 16838var _mediaError = _dereq_(46); 16839 16840var _mediaError2 = _interopRequireDefault(_mediaError); 16841 16842var _window = _dereq_(95); 16843 16844var _window2 = _interopRequireDefault(_window); 16845 16846var _document = _dereq_(94); 16847 16848var _document2 = _interopRequireDefault(_document); 16849 16850var _obj = _dereq_(88); 16851 16852function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 16853 16854function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 16855 16856function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 16857 16858function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 16859 16860function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 16861 * @file tech.js 16862 */ 16863 16864/** 16865 * An Object containing a structure like: `{src: 'url', type: 'mimetype'}` or string 16866 * that just contains the src url alone. 16867 * * `var SourceObject = {src: 'http://ex.com/video.mp4', type: 'video/mp4'};` 16868 * `var SourceString = 'http://example.com/some-video.mp4';` 16869 * 16870 * @typedef {Object|string} Tech~SourceObject 16871 * 16872 * @property {string} src 16873 * The url to the source 16874 * 16875 * @property {string} type 16876 * The mime type of the source 16877 */ 16878 16879/** 16880 * A function used by {@link Tech} to create a new {@link TextTrack}. 16881 * 16882 * @param {Tech} self 16883 * An instance of the Tech class. 16884 * 16885 * @param {string} kind 16886 * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata) 16887 * 16888 * @param {string} [label] 16889 * Label to identify the text track 16890 * 16891 * @param {string} [language] 16892 * Two letter language abbreviation 16893 * 16894 * @param {Object} [options={}] 16895 * An object with additional text track options 16896 * 16897 * @return {TextTrack} 16898 * The text track that was created. 16899 */ 16900function createTrackHelper(self, kind, label, language) { 16901 var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; 16902 16903 var tracks = self.textTracks(); 16904 16905 options.kind = kind; 16906 16907 if (label) { 16908 options.label = label; 16909 } 16910 if (language) { 16911 options.language = language; 16912 } 16913 options.tech = self; 16914 16915 var track = new _textTrack2['default'](options); 16916 16917 tracks.addTrack_(track); 16918 16919 return track; 16920} 16921 16922/** 16923 * This is the base class for media playback technology controllers, such as 16924 * {@link Flash} and {@link HTML5} 16925 * 16926 * @extends Component 16927 */ 16928 16929var Tech = function (_Component) { 16930 _inherits(Tech, _Component); 16931 16932 /** 16933 * Create an instance of this Tech. 16934 * 16935 * @param {Object} [options] 16936 * The key/value store of player options. 16937 * 16938 * @param {Component~ReadyCallback} ready 16939 * Callback function to call when the `HTML5` Tech is ready. 16940 */ 16941 function Tech() { 16942 var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 16943 var ready = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {}; 16944 16945 _classCallCheck(this, Tech); 16946 16947 // we don't want the tech to report user activity automatically.
16948 // This is done manually in addControlsListeners 16949 options.reportTouchActivity = false; 16950 16951 // keep track of whether the current source has played at all to 16952 // implement a very limited played() 16953 var _this = _possibleConstructorReturn(this, _Component.call(this, null, options, ready)); 16954 16955 _this.hasStarted_ = false; 16956 _this.on('playing', function () { 16957 this.hasStarted_ = true; 16958 }); 16959 _this.on('loadstart', function () { 16960 this.hasStarted_ = false; 16961 }); 16962 16963 _this.textTracks_ = options.textTracks; 16964 _this.videoTracks_ = options.videoTracks; 16965 _this.audioTracks_ = options.audioTracks; 16966 16967 // Manually track progress in cases where the browser/flash player doesn't report it. 16968 if (!_this.featuresProgressEvents) { 16969 _this.manualProgressOn(); 16970 } 16971 16972 // Manually track timeupdates in cases where the browser/flash player doesn't report it. 16973 if (!_this.featuresTimeupdateEvents) { 16974 _this.manualTimeUpdatesOn(); 16975 } 16976 16977 ['Text', 'Audio', 'Video'].forEach(function (track) { 16978 if (options['native' + track + 'Tracks'] === false) { 16979 _this['featuresNative' + track + 'Tracks'] = false; 16980 } 16981 }); 16982 16983 if (options.nativeCaptions === false) { 16984 _this.featuresNativeTextTracks = false; 16985 } 16986 16987 if (!_this.featuresNativeTextTracks) { 16988 _this.emulateTextTracks(); 16989 } 16990 16991 _this.autoRemoteTextTracks_ = new _textTrackList2['default'](); 16992 16993 _this.initTextTrackListeners(); 16994 _this.initTrackListeners(); 16995 16996 // Turn on component tap events only if not using native controls 16997 if (!options.nativeControlsForTouch) { 16998 _this.emitTapEvents(); 16999 } 17000 17001 if (_this.constructor) { 17002 _this.name_ = _this.constructor.name || 'Unknown Tech'; 17003 } 17004 return _this; 17005 } 17006 17007 /* Fallbacks for unsupported event types 17008 ================================================================================ */ 17009 17010 /** 17011 * Polyfill the `progress` event for browsers that don't support it natively. 17012 * 17013 * @see {@link Tech#trackProgress} 17014 */ 17015 17016 17017 Tech.prototype.manualProgressOn = function manualProgressOn() { 17018 this.on('durationchange', this.onDurationChange); 17019 17020 this.manualProgress = true; 17021 17022 // Trigger progress watching when a source begins loading 17023 this.one('ready', this.trackProgress); 17024 }; 17025 17026 /** 17027 * Turn off the polyfill for `progress` events that was created in 17028 * {@link Tech#manualProgressOn} 17029 */ 17030 17031 17032 Tech.prototype.manualProgressOff = function manualProgressOff() { 17033 this.manualProgress = false; 17034 this.stopTrackingProgress(); 17035 17036 this.off('durationchange', this.onDurationChange); 17037 }; 17038 17039 /** 17040 * This is used to trigger a `progress` event when the buffered percent changes. It 17041 * sets an interval function that will be called every 500 milliseconds to check if the 17042 * buffer end percent has changed. 17043 * 17044 * > This function is called by {@link Tech#manualProgressOn} 17045 * 17046 * @param {EventTarget~Event} event 17047 * The `ready` event that caused this to run. 17048 * 17049 * @listens Tech#ready 17050 * @fires Tech#progress 17051 */ 17052 17053 17054 Tech.prototype.trackProgress = function trackProgress(event) { 17055 this.stopTrackingProgress(); 17056 this.progressInterval = this.setInterval(Fn.bind(this, function () { 17057 // Don't trigger unless buffered amount is greater than last time 17058 17059 var numBufferedPercent = this.bufferedPercent(); 17060 17061 if (this.bufferedPercent_ !== numBufferedPercent) { 17062 /** 17063 * See {@link Player#progress} 17064 * 17065 * @event Tech#progress 17066 * @type {EventTarget~Event} 17067 */ 17068 this.trigger('progress'); 17069 } 17070 17071 this.bufferedPercent_ = numBufferedPercent; 17072 17073 if (numBufferedPercent === 1) { 17074 this.stopTrackingProgress(); 17075 } 17076 }), 500); 17077 }; 17078 17079 /** 17080 * Update our internal duration on a `durationchange` event by calling 17081 * {@link Tech#duration}. 17082 * 17083 * @param {EventTarget~Event} event 17084 * The `durationchange` event that caused this to run. 17085 * 17086 * @listens Tech#durationchange 17087 */ 17088 17089 17090 Tech.prototype.onDurationChange = function onDurationChange(event) { 17091 this.duration_ = this.duration(); 17092 }; 17093 17094 /** 17095 * Get and create a `TimeRange` object for buffering. 17096 * 17097 * @return {TimeRange} 17098 * The time range object that was created. 17099 */ 17100 17101 17102 Tech.prototype.buffered = function buffered() { 17103 return (0, _timeRanges.createTimeRange)(0, 0); 17104 }; 17105 17106 /** 17107 * Get the percentage of the current video that is currently buffered. 17108 * 17109 * @return {number} 17110 * A number from 0 to 1 that represents the decimal percentage of the 17111 * video that is buffered. 17112 * 17113 */ 17114 17115 17116 Tech.prototype.bufferedPercent = function bufferedPercent() { 17117 return (0, _buffer.bufferedPercent)(this.buffered(), this.duration_); 17118 }; 17119 17120 /** 17121 * Turn off the polyfill for `progress` events that was created in 17122 * {@link Tech#manualProgressOn} 17123 * Stop manually tracking progress events by clearing the interval that was set in 17124 * {@link Tech#trackProgress}. 17125 */ 17126 17127 17128 Tech.prototype.stopTrackingProgress = function stopTrackingProgress() { 17129 this.clearInterval(this.progressInterval); 17130 }; 17131 17132 /** 17133 * Polyfill the `timeupdate` event for browsers that don't support it. 17134 * 17135 * @see {@link Tech#trackCurrentTime} 17136 */ 17137 17138 17139 Tech.prototype.manualTimeUpdatesOn = function manualTimeUpdatesOn() { 17140 this.manualTimeUpdates = true; 17141 17142 this.on('play', this.trackCurrentTime); 17143 this.on('pause', this.stopTrackingCurrentTime); 17144 }; 17145 17146 /** 17147 * Turn off the polyfill for `timeupdate` events that was created in 17148 * {@link Tech#manualTimeUpdatesOn} 17149 */ 17150 17151 17152 Tech.prototype.manualTimeUpdatesOff = function manualTimeUpdatesOff() { 17153 this.manualTimeUpdates = false;
17154 this.stopTrackingCurrentTime(); 17155 this.off('play', this.trackCurrentTime); 17156 this.off('pause', this.stopTrackingCurrentTime); 17157 }; 17158 17159 /** 17160 * Sets up an interval function to track current time and trigger `timeupdate` every 17161 * 250 milliseconds. 17162 * 17163 * @listens Tech#play 17164 * @triggers Tech#timeupdate 17165 */ 17166 17167 17168 Tech.prototype.trackCurrentTime = function trackCurrentTime() { 17169 if (this.currentTimeInterval) { 17170 this.stopTrackingCurrentTime(); 17171 } 17172 this.currentTimeInterval = this.setInterval(function () { 17173 /** 17174 * Triggered at an interval of 250ms to indicated that time is passing in the video. 17175 * 17176 * @event Tech#timeupdate 17177 * @type {EventTarget~Event} 17178 */ 17179 this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true }); 17180 17181 // 42 = 24 fps // 250 is what Webkit uses // FF uses 15 17182 }, 250); 17183 }; 17184 17185 /** 17186 * Stop the interval function created in {@link Tech#trackCurrentTime} so that the 17187 * `timeupdate` event is no longer triggered. 17188 * 17189 * @listens {Tech#pause} 17190 */ 17191 17192 17193 Tech.prototype.stopTrackingCurrentTime = function stopTrackingCurrentTime() { 17194 this.clearInterval(this.currentTimeInterval); 17195 17196 // #1002 - if the video ends right before the next timeupdate would happen, 17197 // the progress bar won't make it all the way to the end 17198 this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true }); 17199 }; 17200 17201 /** 17202 * Turn off all event polyfills, clear the `Tech`s {@link AudioTrackList}, 17203 * {@link VideoTrackList}, and {@link TextTrackList}, and dispose of this Tech. 17204 * 17205 * @fires Component#dispose 17206 */ 17207 17208 17209 Tech.prototype.dispose = function dispose() { 17210 17211 // clear out all tracks because we can't reuse them between techs 17212 this.clearTracks(['audio', 'video', 'text']); 17213 17214 // Turn off any manual progress or timeupdate tracking 17215 if (this.manualProgress) { 17216 this.manualProgressOff(); 17217 } 17218 17219 if (this.manualTimeUpdates) { 17220 this.manualTimeUpdatesOff(); 17221 } 17222 17223 _Component.prototype.dispose.call(this); 17224 }; 17225 17226 /** 17227 * Clear out a single `TrackList` or an array of `TrackLists` given their names. 17228 * 17229 * > Note: Techs without source handlers should call this between sources for `video` 17230 * & `audio` tracks. You don't want to use them between tracks! 17231 * 17232 * @param {string[]|string} types 17233 * TrackList names to clear, valid names are `video`, `audio`, and 17234 * `text`. 17235 */ 17236 17237 17238 Tech.prototype.clearTracks = function clearTracks(types) { 17239 var _this2 = this; 17240 17241 types = [].concat(types); 17242 // clear out all tracks because we can't reuse them between techs 17243 types.forEach(function (type) { 17244 var list = _this2[type + 'Tracks']() || []; 17245 var i = list.length; 17246 17247 while (i--) { 17248 var track = list[i]; 17249 17250 if (type === 'text') { 17251 _this2.removeRemoteTextTrack(track); 17252 } 17253 list.removeTrack_(track); 17254 } 17255 }); 17256 }; 17257 17258 /** 17259 * Remove any TextTracks added via addRemoteTextTrack that are 17260 * flagged for automatic garbage collection 17261 */ 17262 17263 17264 Tech.prototype.cleanupAutoTextTracks = function cleanupAutoTextTracks() { 17265 var list = this.autoRemoteTextTracks_ || []; 17266 var i = list.length; 17267 17268 while (i--) { 17269 var track = list[i]; 17270 17271 this.removeRemoteTextTrack(track); 17272 } 17273 }; 17274 17275 /** 17276 * Reset the tech, which will removes all sources and reset the internal readyState. 17277 * 17278 * @abstract 17279 */ 17280 17281 17282 Tech.prototype.reset = function reset() {}; 17283 17284 /** 17285 * Get or set an error on the Tech. 17286 * 17287 * @param {MediaError} [err] 17288 * Error to set on the Tech 17289 * 17290 * @return {MediaError|null} 17291 * The current error object on the tech, or null if there isn't one. 17292 */ 17293 17294 17295 Tech.prototype.error = function error(err) { 17296 if (err !== undefined) { 17297 this.error_ = new _mediaError2['default'](err); 17298 this.trigger('error'); 17299 } 17300 return this.error_; 17301 }; 17302 17303 /** 17304 * Returns the `TimeRange`s that have been played through for the current source. 17305 * 17306 * > NOTE: This implementation is incomplete. It does not track the played `TimeRange`. 17307 * It only checks wether the source has played at all or not. 17308 * 17309 * @return {TimeRange} 17310 * - A single time range if this video has played 17311 * - An empty set of ranges if not. 17312 */ 17313 17314 17315 Tech.prototype.played = function played() { 17316 if (this.hasStarted_) { 17317 return (0, _timeRanges.createTimeRange)(0, 0); 17318 } 17319 return (0, _timeRanges.createTimeRange)(); 17320 }; 17321 17322 /** 17323 * Causes a manual time update to occur if {@link Tech#manualTimeUpdatesOn} was 17324 * previously called. 17325 * 17326 * @fires Tech#timeupdate 17327 */ 17328 17329 17330 Tech.prototype.setCurrentTime = function setCurrentTime() { 17331 // improve the accuracy of manual timeupdates 17332 if (this.manualTimeUpdates) { 17333 /** 17334 * A manual `timeupdate` event. 17335 * 17336 * @event Tech#timeupdate 17337 * @type {EventTarget~Event} 17338 */ 17339 this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true }); 17340 } 17341 }; 17342 17343 /** 17344 * Turn on listeners for {@link TextTrackList} events. This adds 17345 * {@link EventTarget~EventListeners} for `texttrackchange`, `addtrack` and 17346 * `removetrack`. 17347 * 17348 * @fires Tech#texttrackchange 17349 */ 17350 17351 17352 Tech.prototype.initTextTrackListeners = function initTextTrackListeners() { 17353 var textTrackListChanges = Fn.bind(this, function () { 17354 /** 17355 * Triggered when tracks are added or removed on the Tech {@link TextTrackList} 17356 * 17357 * @event Tech#texttrackchange 17358 * @type {EventTarget~Event} 17359 */ 17360 this.trigger('texttrackchange'); 17361 }); 17362 17363 var tracks = this.textTracks(); 17364 17365 if (!tracks) { 17366 return; 17367 } 17368 17369 tracks.addEventListener('removetrack', textTrackListChanges); 17370 tracks.addEventListener('addtrack', textTrackListChanges); 17371 17372 this.on('dispose', Fn.bind(this, function () { 17373 tracks.removeEventListener('removetrack', textTrackListChanges); 17374 tracks.removeEventListener('addtrack', textTrackListChanges); 17375 })); 17376 }; 17377 17378 /** 17379 * Turn on listeners for {@link VideoTrackList} and {@link {AudioTrackList} events. 17380 * This adds {@link EventTarget~EventListeners} for `addtrack`, and `removetrack`. 17381 * 17382 * @fires Tech#audiotrackchange 17383 * @fires Tech#videotrackchange 17384 */ 17385 17386 17387 Tech.prototype.initTrackListeners = function initTrackListeners() { 17388 var _this3 = this; 17389 17390 var trackTypes = ['video', 'audio']; 17391
17392 trackTypes.forEach(function (type) { 17393 /** 17394 * Triggered when tracks are added or removed on the Tech {@link AudioTrackList} 17395 * 17396 * @event Tech#audiotrackchange 17397 * @type {EventTarget~Event} 17398 */ 17399 17400 /** 17401 * Triggered when tracks are added or removed on the Tech {@link VideoTrackList} 17402 * 17403 * @event Tech#videotrackchange 17404 * @type {EventTarget~Event} 17405 */ 17406 var trackListChanges = function trackListChanges() { 17407 _this3.trigger(type + 'trackchange'); 17408 }; 17409 17410 var tracks = _this3[type + 'Tracks'](); 17411 17412 tracks.addEventListener('removetrack', trackListChanges); 17413 tracks.addEventListener('addtrack', trackListChanges); 17414 17415 _this3.on('dispose', function () { 17416 tracks.removeEventListener('removetrack', trackListChanges); 17417 tracks.removeEventListener('addtrack', trackListChanges); 17418 }); 17419 }); 17420 }; 17421 17422 /** 17423 * Emulate TextTracks using vtt.js if necessary 17424 * 17425 * @fires Tech#vttjsloaded 17426 * @fires Tech#vttjserror 17427 */ 17428 17429 17430 Tech.prototype.addWebVttScript_ = function addWebVttScript_() { 17431 var _this4 = this; 17432 17433 if (_window2['default'].WebVTT) { 17434 return; 17435 } 17436 17437 // Initially, Tech.el_ is a child of a dummy-div wait until the Component system 17438 // signals that the Tech is ready at which point Tech.el_ is part of the DOM 17439 // before inserting the WebVTT script 17440 if (_document2['default'].body.contains(this.el())) { 17441 var vtt = _dereq_(99); 17442 17443 // load via require if available and vtt.js script location was not passed in 17444 // as an option. novtt builds will turn the above require call into an empty object 17445 // which will cause this if check to always fail. 17446 if (!this.options_['vtt.js'] && (0, _obj.isPlain)(vtt) && Object.keys(vtt).length > 0) { 17447 this.trigger('vttjsloaded'); 17448 return; 17449 } 17450 17451 // load vtt.js via the script location option or the cdn of no location was 17452 // passed in 17453 var script = _document2['default'].createElement('script'); 17454 17455 script.src = this.options_['vtt.js'] || 'https://vjs.zencdn.net/vttjs/0.12.4/vtt.min.js'; 17456 script.onload = function () { 17457 /** 17458 * Fired when vtt.js is loaded. 17459 * 17460 * @event Tech#vttjsloaded 17461 * @type {EventTarget~Event} 17462 */ 17463 _this4.trigger('vttjsloaded'); 17464 }; 17465 script.onerror = function () { 17466 /** 17467 * Fired when vtt.js was not loaded due to an error 17468 * 17469 * @event Tech#vttjsloaded 17470 * @type {EventTarget~Event} 17471 */ 17472 _this4.trigger('vttjserror'); 17473 }; 17474 this.on('dispose', function () { 17475 script.onload = null; 17476 script.onerror = null; 17477 }); 17478 // but have not loaded yet and we set it to true before the inject so that 17479 // we don't overwrite the injected window.WebVTT if it loads right away 17480 _window2['default'].WebVTT = true; 17481 this.el().parentNode.appendChild(script); 17482 } else { 17483 this.ready(this.addWebVttScript_); 17484 } 17485 }; 17486 17487 /** 17488 * Emulate texttracks 17489 * 17490 * @method emulateTextTracks 17491 */ 17492 17493 17494 Tech.prototype.emulateTextTracks = function emulateTextTracks() { 17495 var _this5 = this; 17496 17497 var tracks = this.textTracks(); 17498 17499 if (!tracks) { 17500 return; 17501 } 17502 17503 var remoteTracks = this.remoteTextTracks(); 17504 var handleAddTrack = function handleAddTrack(e) { 17505 return tracks.addTrack_(e.track); 17506 }; 17507 var handleRemoveTrack = function handleRemoveTrack(e) { 17508 return tracks.removeTrack_(e.track); 17509 }; 17510 17511 remoteTracks.on('addtrack', handleAddTrack); 17512 remoteTracks.on('removetrack', handleRemoveTrack); 17513 17514 this.addWebVttScript_(); 17515 17516 var updateDisplay = function updateDisplay() { 17517 return _this5.trigger('texttrackchange'); 17518 }; 17519 17520 var textTracksChanges = function textTracksChanges() { 17521 updateDisplay(); 17522 17523 for (var i = 0; i < tracks.length; i++) { 17524 var track = tracks[i]; 17525 17526 track.removeEventListener('cuechange', updateDisplay); 17527 if (track.mode === 'showing') { 17528 track.addEventListener('cuechange', updateDisplay); 17529 } 17530 } 17531 }; 17532 17533 textTracksChanges(); 17534 tracks.addEventListener('change', textTracksChanges); 17535 tracks.addEventListener('addtrack', textTracksChanges); 17536 tracks.addEventListener('removetrack', textTracksChanges); 17537 17538 this.on('dispose', function () { 17539 remoteTracks.off('addtrack', handleAddTrack); 17540 remoteTracks.off('removetrack', handleRemoveTrack); 17541 tracks.removeEventListener('change', textTracksChanges); 17542 tracks.removeEventListener('addtrack', textTracksChanges); 17543 tracks.removeEventListener('removetrack', textTracksChanges); 17544 17545 for (var i = 0; i < tracks.length; i++) { 17546 var track = tracks[i]; 17547 17548 track.removeEventListener('cuechange', updateDisplay); 17549 } 17550 }); 17551 }; 17552 17553 /** 17554 * Get the `Tech`s {@link VideoTrackList}. 17555 * 17556 * @return {VideoTrackList} 17557 * The video track list that the Tech is currently using. 17558 */ 17559 17560 17561 Tech.prototype.videoTracks = function videoTracks() { 17562 this.videoTracks_ = this.videoTracks_ || new _videoTrackList2['default'](); 17563 return this.videoTracks_; 17564 }; 17565 17566 /** 17567 * Get the `Tech`s {@link AudioTrackList}. 17568 * 17569 * @return {AudioTrackList} 17570 * The audio track list that the Tech is currently using. 17571 */ 17572 17573 17574 Tech.prototype.audioTracks = function audioTracks() { 17575 this.audioTracks_ = this.audioTracks_ || new _audioTrackList2['default'](); 17576 return this.audioTracks_; 17577 }; 17578 17579 /** 17580 * Get the `Tech`s {@link TextTrackList}. 17581 * 17582 * @return {TextTrackList} 17583 * The text track list that the Tech is currently using. 17584 */ 17585 17586 17587 Tech.prototype.textTracks = function textTracks() { 17588 this.textTracks_ = this.textTracks_ || new _textTrackList2['default'](); 17589 return this.textTracks_; 17590 }; 17591 17592 /** 17593 * Get the `Tech`s remote {@link TextTrackList}, which is created from elements 17594 * that were added to the DOM. 17595 * 17596 * @return {TextTrackList} 17597 * The remote text track list that the Tech is currently using. 17598 */ 17599 17600 17601 Tech.prototype.remoteTextTracks = function remoteTextTracks() { 17602 this.remoteTextTracks_ = this.remoteTextTracks_ || new _textTrackList2['default'](); 17603 return this.remoteTextTracks_; 17604 }; 17605 17606 /** 17607 * Get The `Tech`s {HTMLTrackElementList}, which are the elements in the DOM that are 17608 * being used as TextTracks. 17609 * 17610 * @return {HTMLTrackElementList} 17611 * The current HTML track elements that exist for the tech. 17612 */ 17613 17614 17615 Tech.prototype.remoteTextTrackEls = function remoteTextTrackEls() { 17616 this.remoteTextTrackEls_ = this.remoteTextTrackEls_ || new _htmlTrackElementList2['default'](); 17617 return this.remoteTextTrackEls_;
17618 }; 17619 17620 /** 17621 * Create and returns a remote {@link TextTrack} object. 17622 * 17623 * @param {string} kind 17624 * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata) 17625 * 17626 * @param {string} [label] 17627 * Label to identify the text track 17628 * 17629 * @param {string} [language] 17630 * Two letter language abbreviation 17631 * 17632 * @return {TextTrack} 17633 * The TextTrack that gets created. 17634 */ 17635 17636 17637 Tech.prototype.addTextTrack = function addTextTrack(kind, label, language) { 17638 if (!kind) { 17639 throw new Error('TextTrack kind is required but was not provided'); 17640 } 17641 17642 return createTrackHelper(this, kind, label, language); 17643 }; 17644 17645 /** 17646 * Create an emulated TextTrack for use by addRemoteTextTrack 17647 * 17648 * This is intended to be overridden by classes that inherit from 17649 * Tech in order to create native or custom TextTracks. 17650 * 17651 * @param {Object} options 17652 * The object should contain the options to initialize the TextTrack with. 17653 * 17654 * @param {string} [options.kind] 17655 * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata). 17656 * 17657 * @param {string} [options.label]. 17658 * Label to identify the text track 17659 * 17660 * @param {string} [options.language] 17661 * Two letter language abbreviation. 17662 * 17663 * @return {HTMLTrackElement} 17664 * The track element that gets created. 17665 */ 17666 17667 17668 Tech.prototype.createRemoteTextTrack = function createRemoteTextTrack(options) { 17669 var track = (0, _mergeOptions2['default'])(options, { 17670 tech: this 17671 }); 17672 17673 return new _htmlTrackElement2['default'](track); 17674 }; 17675 17676 /** 17677 * Creates a remote text track object and returns an html track element. 17678 * 17679 * > Note: This can be an emulated {@link HTMLTrackElement} or a native one. 17680 * 17681 * @param {Object} options 17682 * See {@link Tech#createRemoteTextTrack} for more detailed properties. 17683 * 17684 * @param {boolean} [manualCleanup=true] 17685 * - When false: the TextTrack will be automatically removed from the video 17686 * element whenever the source changes 17687 * - When True: The TextTrack will have to be cleaned up manually 17688 * 17689 * @return {HTMLTrackElement} 17690 * An Html Track Element. 17691 * 17692 * @deprecated The default functionality for this function will be equivalent 17693 * to "manualCleanup=false" in the future. The manualCleanup parameter will 17694 * also be removed. 17695 */ 17696 17697 17698 Tech.prototype.addRemoteTextTrack = function addRemoteTextTrack() { 17699 var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 17700 var manualCleanup = arguments[1]; 17701 17702 var htmlTrackElement = this.createRemoteTextTrack(options); 17703 17704 if (manualCleanup !== true && manualCleanup !== false) { 17705 // deprecation warning 17706 _log2['default'].warn('Calling addRemoteTextTrack without explicitly setting the "manualCleanup" parameter to `true` is deprecated and default to `false` in future version of video.js'); 17707 manualCleanup = true; 17708 } 17709 17710 // store HTMLTrackElement and TextTrack to remote list 17711 this.remoteTextTrackEls().addTrackElement_(htmlTrackElement); 17712 this.remoteTextTracks().addTrack_(htmlTrackElement.track); 17713 17714 if (manualCleanup !== true) { 17715 // create the TextTrackList if it doesn't exist 17716 this.autoRemoteTextTracks_.addTrack_(htmlTrackElement.track); 17717 } 17718 17719 return htmlTrackElement; 17720 }; 17721 17722 /** 17723 * Remove a remote text track from the remote `TextTrackList`. 17724 * 17725 * @param {TextTrack} track 17726 * `TextTrack` to remove from the `TextTrackList` 17727 */ 17728 17729 17730 Tech.prototype.removeRemoteTextTrack = function removeRemoteTextTrack(track) { 17731 var trackElement = this.remoteTextTrackEls().getTrackElementByTrack_(track); 17732 17733 // remove HTMLTrackElement and TextTrack from remote list 17734 this.remoteTextTrackEls().removeTrackElement_(trackElement); 17735 this.remoteTextTracks().removeTrack_(track); 17736 this.autoRemoteTextTracks_.removeTrack_(track); 17737 }; 17738 17739 /** 17740 * Gets available media playback quality metrics as specified by the W3C's Media 17741 * Playback Quality API. 17742 * 17743 * @see [Spec]{@link https://wicg.github.io/media-playback-quality} 17744 * 17745 * @return {Object} 17746 * An object with supported media playback quality metrics 17747 * 17748 * @abstract 17749 */ 17750 17751 17752 Tech.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() { 17753 return {}; 17754 }; 17755 17756 /** 17757 * A method to set a poster from a `Tech`. 17758 * 17759 * @abstract 17760 */ 17761 17762 17763 Tech.prototype.setPoster = function setPoster() {}; 17764 17765 /** 17766 * A method to check for the presence of the 'playsinine' <video> attribute. 17767 * 17768 * @abstract 17769 */ 17770 17771 17772 Tech.prototype.playsinline = function playsinline() {}; 17773 17774 /** 17775 * A method to set or unset the 'playsinine' <video> attribute. 17776 * 17777 * @abstract 17778 */ 17779 17780 17781 Tech.prototype.setPlaysinline = function setPlaysinline() {}; 17782 17783 /* 17784 * Check if the tech can support the given mime-type. 17785 * 17786 * The base tech does not support any type, but source handlers might 17787 * overwrite this. 17788 * 17789 * @param {string} type 17790 * The mimetype to check for support 17791 * 17792 * @return {string} 17793 * 'probably', 'maybe', or empty string 17794 * 17795 * @see [Spec]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canPlayType} 17796 * 17797 * @abstract 17798 */ 17799 17800 17801 Tech.prototype.canPlayType = function canPlayType() { 17802 return ''; 17803 }; 17804 17805 /* 17806 * Return whether the argument is a Tech or not. 17807 * Can be passed either a Class like `Html5` or a instance like `player.tech_` 17808 * 17809 * @param {Object} component 17810 * The item to check 17811 * 17812 * @return {boolean} 17813 * Whether it is a tech or not 17814 * - True if it is a tech 17815 * - False if it is not 17816 */ 17817 17818 17819 Tech.isTech = function isTech(component) { 17820 return component.prototype instanceof Tech || component instanceof Tech || component === Tech; 17821 }; 17822 17823 /** 17824 * Registers a `Tech` into a shared list for videojs. 17825 * 17826 * @param {string} name 17827 * Name of the `Tech` to register. 17828 * 17829 * @param {Object} tech 17830 * The `Tech` class to register. 17831 */ 17832 17833 17834 Tech.registerTech = function registerTech(name, tech) { 17835 if (!Tech.techs_) { 17836 Tech.techs_ = {}; 17837 } 17838 17839 if (!Tech.isTech(tech)) { 17840 throw new Error('Tech ' + name + ' must be a Tech'); 17841 } 17842 17843 Tech.techs_[name] = tech; 17844 return tech; 17845 }; 17846 17847 /** 17848 * Get a `Tech` from the shared list by name. 17849 * 17850 * @param {string} name 17851 * Name of the component to get 17852 * 17853 * @return {Tech|undefined} 17854 * The `Tech` or undefined if there was no tech with the name requsted. 17855 */ 17856 17857 17858 Tech.getTech = function getTech(name) {
17859 if (Tech.techs_ && Tech.techs_[name]) { 17860 return Tech.techs_[name]; 17861 } 17862 17863 if (_window2['default'] && _window2['default'].videojs && _window2['default'].videojs[name]) { 17864 _log2['default'].warn('The ' + name + ' tech was added to the videojs object when it should be registered using videojs.registerTech(name, tech)'); 17865 return _window2['default'].videojs[name]; 17866 } 17867 }; 17868 17869 return Tech; 17870}(_component2['default']); 17871 17872/** 17873 * List of associated text tracks. 17874 * 17875 * @type {TextTrackList} 17876 * @private 17877 */ 17878 17879 17880Tech.prototype.textTracks_; // eslint-disable-line 17881 17882/** 17883 * List of associated audio tracks. 17884 * 17885 * @type {AudioTrackList} 17886 * @private 17887 */ 17888Tech.prototype.audioTracks_; // eslint-disable-line 17889 17890/** 17891 * List of associated video tracks. 17892 * 17893 * @type {VideoTrackList} 17894 * @private 17895 */ 17896Tech.prototype.videoTracks_; // eslint-disable-line 17897 17898/** 17899 * Boolean indicating wether the `Tech` supports volume control. 17900 * 17901 * @type {boolean} 17902 * @default 17903 */ 17904Tech.prototype.featuresVolumeControl = true; 17905 17906/** 17907 * Boolean indicating wether the `Tech` support fullscreen resize control. 17908 * Resizing plugins using request fullscreen reloads the plugin 17909 * 17910 * @type {boolean} 17911 * @default 17912 */ 17913Tech.prototype.featuresFullscreenResize = false; 17914 17915/** 17916 * Boolean indicating wether the `Tech` supports changing the speed at which the video 17917 * plays. Examples: 17918 * - Set player to play 2x (twice) as fast 17919 * - Set player to play 0.5x (half) as fast 17920 * 17921 * @type {boolean} 17922 * @default 17923 */ 17924Tech.prototype.featuresPlaybackRate = false; 17925 17926/** 17927 * Boolean indicating wether the `Tech` supports the `progress` event. This is currently 17928 * not triggered by video-js-swf. This will be used to determine if 17929 * {@link Tech#manualProgressOn} should be called. 17930 * 17931 * @type {boolean} 17932 * @default 17933 */ 17934Tech.prototype.featuresProgressEvents = false; 17935 17936/** 17937 * Boolean indicating wether the `Tech` supports the `timeupdate` event. This is currently 17938 * not triggered by video-js-swf. This will be used to determine if 17939 * {@link Tech#manualTimeUpdates} should be called. 17940 * 17941 * @type {boolean} 17942 * @default 17943 */ 17944Tech.prototype.featuresTimeupdateEvents = false; 17945 17946/** 17947 * Boolean indicating wether the `Tech` supports the native `TextTrack`s. 17948 * This will help us integrate with native `TextTrack`s if the browser supports them. 17949 * 17950 * @type {boolean} 17951 * @default 17952 */ 17953Tech.prototype.featuresNativeTextTracks = false; 17954 17955/** 17956 * A functional mixin for techs that want to use the Source Handler pattern. 17957 * Source handlers are scripts for handling specific formats. 17958 * The source handler pattern is used for adaptive formats (HLS, DASH) that 17959 * manually load video data and feed it into a Source Buffer (Media Source Extensions) 17960 * Example: `Tech.withSourceHandlers.call(MyTech);` 17961 * 17962 * @param {Tech} _Tech 17963 * The tech to add source handler functions to. 17964 * 17965 * @mixes Tech~SourceHandlerAdditions 17966 */ 17967Tech.withSourceHandlers = function (_Tech) { 17968 17969 /** 17970 * Register a source handler 17971 * 17972 * @param {Function} handler 17973 * The source handler class 17974 * 17975 * @param {number} [index] 17976 * Register it at the following index 17977 */ 17978 _Tech.registerSourceHandler = function (handler, index) { 17979 var handlers = _Tech.sourceHandlers; 17980 17981 if (!handlers) { 17982 handlers = _Tech.sourceHandlers = []; 17983 } 17984 17985 if (index === undefined) { 17986 // add to the end of the list 17987 index = handlers.length; 17988 } 17989 17990 handlers.splice(index, 0, handler); 17991 }; 17992 17993 /** 17994 * Check if the tech can support the given type. Also checks the 17995 * Techs sourceHandlers. 17996 * 17997 * @param {string} type 17998 * The mimetype to check. 17999 * 18000 * @return {string} 18001 * 'probably', 'maybe', or '' (empty string) 18002 */ 18003 _Tech.canPlayType = function (type) { 18004 var handlers = _Tech.sourceHandlers || []; 18005 var can = void 0; 18006 18007 for (var i = 0; i < handlers.length; i++) { 18008 can = handlers[i].canPlayType(type); 18009 18010 if (can) { 18011 return can; 18012 } 18013 } 18014 18015 return ''; 18016 }; 18017 18018 /** 18019 * Returns the first source handler that supports the source. 18020 * 18021 * TODO: Answer question: should 'probably' be prioritized over 'maybe' 18022 * 18023 * @param {Tech~SourceObject} source 18024 * The source object 18025 * 18026 * @param {Object} options 18027 * The options passed to the tech 18028 * 18029 * @return {SourceHandler|null} 18030 * The first source handler that supports the source or null if 18031 * no SourceHandler supports the source 18032 */ 18033 _Tech.selectSourceHandler = function (source, options) { 18034 var handlers = _Tech.sourceHandlers || []; 18035 var can = void 0; 18036 18037 for (var i = 0; i < handlers.length; i++) { 18038 can = handlers[i].canHandleSource(source, options); 18039 18040 if (can) { 18041 return handlers[i]; 18042 } 18043 } 18044 18045 return null; 18046 }; 18047 18048 /** 18049 * Check if the tech can support the given source. 18050 * 18051 * @param {Tech~SourceObject} srcObj 18052 * The source object 18053 * 18054 * @param {Object} options 18055 * The options passed to the tech 18056 * 18057 * @return {string} 18058 * 'probably', 'maybe', or '' (empty string) 18059 */ 18060 _Tech.canPlaySource = function (srcObj, options) { 18061 var sh = _Tech.selectSourceHandler(srcObj, options); 18062 18063 if (sh) { 18064 return sh.canHandleSource(srcObj, options); 18065 } 18066 18067 return ''; 18068 }; 18069 18070 /** 18071 * When using a source handler, prefer its implementation of 18072 * any function normally provided by the tech. 18073 */ 18074 var deferrable = ['seekable', 'duration']; 18075 18076 /** 18077 * A wrapper around {@link Tech#seekable} that will call a `SourceHandler`s seekable 18078 * function if it exists, with a fallback to the Techs seekable function. 18079 * 18080 * @method _Tech.seekable 18081 */ 18082 18083 /** 18084 * A wrapper around {@link Tech#duration} that will call a `SourceHandler`s duration 18085 * function if it exists, otherwise it will fallback to the techs duration function. 18086 * 18087 * @method _Tech.duration 18088 */ 18089 18090 deferrable.forEach(function (fnName) { 18091 var originalFn = this[fnName]; 18092 18093 if (typeof originalFn !== 'function') { 18094 return; 18095 } 18096 18097 this[fnName] = function () { 18098 if (this.sourceHandler_ && this.sourceHandler_[fnName]) { 18099 return this.sourceHandler_[fnName].apply(this.sourceHandler_, arguments); 18100 } 18101 return originalFn.apply(this, arguments); 18102 }; 18103 }, _Tech.prototype); 18104 18105 /** 18106 * Create a function for setting the source using a source object 18107 * and source handlers. 18108 * Should never be called unless a source handler was found. 18109 * 18110 * @param {Tech~SourceObject} source 18111 * A source object with src and type keys 18112 * 18113 * @return {Tech} 18114 * Returns itself; this method is chainable 18115 */ 18116 _Tech.prototype.setSource = function (source) { 18117 var sh = _Tech.selectSourceHandler(source, this.options_); 18118 18119 if (!sh) { 18120 // Fall back to a native source hander when unsupported sources are 18121 // deliberately set 18122 if (_Tech.nativeSourceHandler) { 18123 sh = _Tech.nativeSourceHandler; 18124 } else { 18125 _log2['default'].error('No source hander found for the current source.'); 18126 } 18127 } 18128 18129 // Dispose any existing source handler 18130 this.disposeSourceHandler(); 18131 this.off('dispose', this.disposeSourceHandler); 18132 18133 if (sh !== _Tech.nativeSourceHandler) { 18134 this.currentSource_ = source; 18135 18136 // Catch if someone replaced the src without calling setSource. 18137 // If they do, set currentSource_ to null and dispose our source handler. 18138 this.off(this.el_, 'loadstart', _Tech.prototype.firstLoadStartListener_); 18139 this.off(this.el_, 'loadstart', _Tech.prototype.successiveLoadStartListener_); 18140 this.one(this.el_, 'loadstart', _Tech.prototype.firstLoadStartListener_); 18141 } 18142 18143 this.sourceHandler_ = sh.handleSource(source, this, this.options_); 18144 this.on('dispose', this.disposeSourceHandler); 18145 18146 return this; 18147 }; 18148 18149 /** 18150 * Called once for the first loadstart of a video. 18151 * 18152 * @listens Tech#loadstart 18153 */ 18154 _Tech.prototype.firstLoadStartListener_ = function () { 18155 this.one(this.el_, 'loadstart', _Tech.prototype.successiveLoadStartListener_); 18156 }; 18157 18158 // On successive loadstarts when setSource has not been called again 18159 /** 18160 * Called after the first loadstart for a video occurs. 18161 * 18162 * @listens Tech#loadstart 18163 */ 18164 _Tech.prototype.successiveLoadStartListener_ = function () { 18165 this.disposeSourceHandler(); 18166 this.one(this.el_, 'loadstart', _Tech.prototype.successiveLoadStartListener_); 18167 }; 18168 18169 /** 18170 * Clean up any existing SourceHandlers and listeners when the Tech is disposed. 18171 * 18172 * @listens Tech#dispose 18173 */ 18174 _Tech.prototype.disposeSourceHandler = function () { 18175 // if we have a source and get another one 18176 // then we are loading something new 18177 // than clear all of our current tracks 18178 if (this.currentSource_) { 18179 this.clearTracks(['audio', 'video']); 18180 this.currentSource_ = null; 18181 } 18182 18183 // always clean up auto-text tracks 18184 this.cleanupAutoTextTracks(); 18185 18186 if (this.sourceHandler_) { 18187 this.off(this.el_, 'loadstart', _Tech.prototype.firstLoadStartListener_); 18188 this.off(this.el_, 'loadstart', _Tech.prototype.successiveLoadStartListener_); 18189 18190 if (this.sourceHandler_.dispose) { 18191 this.sourceHandler_.dispose(); 18192 } 18193 18194 this.sourceHandler_ = null; 18195 } 18196 }; 18197}; 18198 18199_component2['default'].registerComponent('Tech', Tech); 18200// Old name for Tech 18201// @deprecated 18202_component2['default'].registerComponent('MediaTechController', Tech);
18203Tech.registerTech('Tech', Tech); 18204exports['default'] = Tech; 18205 18206},{"46":46,"5":5,"63":63,"65":65,"66":66,"70":70,"72":72,"76":76,"79":79,"83":83,"86":86,"87":87,"88":88,"90":90,"94":94,"95":95,"99":99}],63:[function(_dereq_,module,exports){ 18207'use strict'; 18208 18209exports.__esModule = true; 18210 18211var _trackList = _dereq_(74); 18212 18213var _trackList2 = _interopRequireDefault(_trackList); 18214 18215var _browser = _dereq_(78); 18216 18217var browser = _interopRequireWildcard(_browser); 18218 18219var _document = _dereq_(94); 18220 18221var _document2 = _interopRequireDefault(_document); 18222 18223function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 18224 18225function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 18226 18227function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 18228 18229function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 18230 18231function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 18232 * @file audio-track-list.js 18233 */ 18234 18235 18236/** 18237 * Anywhere we call this function we diverge from the spec 18238 * as we only support one enabled audiotrack at a time 18239 * 18240 * @param {AudioTrackList} list 18241 * list to work on 18242 * 18243 * @param {AudioTrack} track 18244 * The track to skip 18245 * 18246 * @private 18247 */ 18248var disableOthers = function disableOthers(list, track) { 18249 for (var i = 0; i < list.length; i++) { 18250 if (track.id === list[i].id) { 18251 continue; 18252 } 18253 // another audio track is enabled, disable it 18254 list[i].enabled = false; 18255 } 18256}; 18257 18258/** 18259 * The current list of {@link AudioTrack} for a media file. 18260 * 18261 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist} 18262 * @extends TrackList 18263 */ 18264 18265var AudioTrackList = function (_TrackList) { 18266 _inherits(AudioTrackList, _TrackList); 18267 18268 /** 18269 * Create an instance of this class. 18270 * 18271 * @param {AudioTrack[]} [tracks=[]] 18272 * A list of `AudioTrack` to instantiate the list with. 18273 */ 18274 function AudioTrackList() { 18275 var _this, _ret; 18276 18277 var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; 18278 18279 _classCallCheck(this, AudioTrackList); 18280 18281 var list = void 0; 18282 18283 // make sure only 1 track is enabled 18284 // sorted from last index to first index 18285 for (var i = tracks.length - 1; i >= 0; i--) { 18286 if (tracks[i].enabled) { 18287 disableOthers(tracks, tracks[i]); 18288 break; 18289 } 18290 } 18291 18292 // IE8 forces us to implement inheritance ourselves 18293 // as it does not support Object.defineProperty properly 18294 if (browser.IS_IE8) { 18295 list = _document2['default'].createElement('custom'); 18296 for (var prop in _trackList2['default'].prototype) { 18297 if (prop !== 'constructor') { 18298 list[prop] = _trackList2['default'].prototype[prop]; 18299 } 18300 } 18301 for (var _prop in AudioTrackList.prototype) { 18302 if (_prop !== 'constructor') { 18303 list[_prop] = AudioTrackList.prototype[_prop]; 18304 } 18305 } 18306 } 18307
18308 list = (_this = _possibleConstructorReturn(this, _TrackList.call(this, tracks, list)), _this); 18309 list.changing_ = false; 18310 18311 return _ret = list, _possibleConstructorReturn(_this, _ret); 18312 } 18313 18314 /** 18315 * Add an {@link AudioTrack} to the `AudioTrackList`. 18316 * 18317 * @param {AudioTrack} track 18318 * The AudioTrack to add to the list 18319 * 18320 * @fires Track#addtrack 18321 * @private 18322 */ 18323 18324 18325 AudioTrackList.prototype.addTrack_ = function addTrack_(track) { 18326 var _this2 = this; 18327 18328 if (track.enabled) { 18329 disableOthers(this, track); 18330 } 18331 18332 _TrackList.prototype.addTrack_.call(this, track); 18333 // native tracks don't have this 18334 if (!track.addEventListener) { 18335 return; 18336 } 18337 18338 /** 18339 * @listens AudioTrack#enabledchange 18340 * @fires TrackList#change 18341 */ 18342 track.addEventListener('enabledchange', function () { 18343 // when we are disabling other tracks (since we don't support 18344 // more than one track at a time) we will set changing_ 18345 // to true so that we don't trigger additional change events 18346 if (_this2.changing_) { 18347 return; 18348 } 18349 _this2.changing_ = true; 18350 disableOthers(_this2, track); 18351 _this2.changing_ = false; 18352 _this2.trigger('change'); 18353 }); 18354 }; 18355 18356 /** 18357 * Add an {@link AudioTrack} to the `AudioTrackList`. 18358 * 18359 * @param {AudioTrack} track 18360 * The AudioTrack to add to the list 18361 * 18362 * @fires Track#addtrack 18363 */ 18364 18365 18366 AudioTrackList.prototype.addTrack = function addTrack(track) { 18367 this.addTrack_(track); 18368 }; 18369 18370 /** 18371 * Remove an {@link AudioTrack} from the `AudioTrackList`. 18372 * 18373 * @param {AudioTrack} track 18374 * The AudioTrack to remove from the list 18375 * 18376 * @fires Track#removetrack 18377 */ 18378 18379 18380 AudioTrackList.prototype.removeTrack = function removeTrack(track) { 18381 _TrackList.prototype.removeTrack_.call(this, track); 18382 }; 18383 18384 return AudioTrackList; 18385}(_trackList2['default']); 18386 18387exports['default'] = AudioTrackList; 18388 18389},{"74":74,"78":78,"94":94}],64:[function(_dereq_,module,exports){ 18390'use strict'; 18391 18392exports.__esModule = true; 18393 18394var _trackEnums = _dereq_(73); 18395 18396var _track = _dereq_(75); 18397 18398var _track2 = _interopRequireDefault(_track); 18399 18400var _mergeOptions = _dereq_(87); 18401 18402var _mergeOptions2 = _interopRequireDefault(_mergeOptions); 18403 18404var _browser = _dereq_(78); 18405 18406var browser = _interopRequireWildcard(_browser); 18407 18408function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 18409 18410function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 18411 18412function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 18413 18414function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 18415 18416function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 18417 18418/** 18419 * A representation of a single `AudioTrack`. If it is part of an {@link AudioTrackList} 18420 * only one `AudioTrack` in the list will be enabled at a time. 18421 * 18422 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotrack} 18423 * @extends Track 18424 */ 18425var AudioTrack = function (_Track) { 18426 _inherits(AudioTrack, _Track); 18427 18428 /** 18429 * Create an instance of this class. 18430 * 18431 * @param {Object} [options={}] 18432 * Object of option names and values 18433 * 18434 * @param {AudioTrack~Kind} [options.kind=''] 18435 * A valid audio track kind 18436 * 18437 * @param {string} [options.id='vjs_track_' + Guid.newGUID()] 18438 * A unique id for this AudioTrack. 18439 * 18440 * @param {string} [options.label=''] 18441 * The menu label for this track. 18442 * 18443 * @param {string} [options.language=''] 18444 * A valid two character language code. 18445 * 18446 * @param {boolean} [options.enabled] 18447 * If this track is the one that is currently playing. If this track is part of 18448 * an {@link AudioTrackList}, only one {@link AudioTrack} will be enabled. 18449 */ 18450 function AudioTrack() { 18451 var _this, _ret; 18452 18453 var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 18454 18455 _classCallCheck(this, AudioTrack); 18456 18457 var settings = (0, _mergeOptions2['default'])(options, { 18458 kind: _trackEnums.AudioTrackKind[options.kind] || '' 18459 }); 18460 // on IE8 this will be a document element 18461 // for every other browser this will be a normal object
18462 var track = (_this = _possibleConstructorReturn(this, _Track.call(this, settings)), _this); 18463 var enabled = false; 18464 18465 if (browser.IS_IE8) { 18466 for (var prop in AudioTrack.prototype) { 18467 if (prop !== 'constructor') { 18468 track[prop] = AudioTrack.prototype[prop]; 18469 } 18470 } 18471 } 18472 /** 18473 * @member {boolean} enabled 18474 * If this `AudioTrack` is enabled or not. When setting this will 18475 * fire {@link AudioTrack#enabledchange} if the state of enabled is changed. 18476 * 18477 * @fires VideoTrack#selectedchange 18478 */ 18479 Object.defineProperty(track, 'enabled', { 18480 get: function get() { 18481 return enabled; 18482 }, 18483 set: function set(newEnabled) { 18484 // an invalid or unchanged value 18485 if (typeof newEnabled !== 'boolean' || newEnabled === enabled) { 18486 return; 18487 } 18488 enabled = newEnabled; 18489 18490 /** 18491 * An event that fires when enabled changes on this track. This allows 18492 * the AudioTrackList that holds this track to act accordingly. 18493 * 18494 * > Note: This is not part of the spec! Native tracks will do 18495 * this internally without an event. 18496 * 18497 * @event AudioTrack#enabledchange 18498 * @type {EventTarget~Event} 18499 */ 18500 this.trigger('enabledchange'); 18501 } 18502 }); 18503 18504 // if the user sets this track to selected then 18505 // set selected to that true value otherwise 18506 // we keep it false 18507 if (settings.enabled) { 18508 track.enabled = settings.enabled; 18509 } 18510 track.loaded_ = true; 18511 18512 return _ret = track, _possibleConstructorReturn(_this, _ret); 18513 } 18514 18515 return AudioTrack; 18516}(_track2['default']); 18517 18518exports['default'] = AudioTrack; 18519 18520},{"73":73,"75":75,"78":78,"87":87}],65:[function(_dereq_,module,exports){ 18521'use strict'; 18522 18523exports.__esModule = true; 18524 18525var _browser = _dereq_(78); 18526 18527var browser = _interopRequireWildcard(_browser); 18528 18529var _document = _dereq_(94); 18530 18531var _document2 = _interopRequireDefault(_document); 18532 18533function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 18534 18535function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 18536 18537function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** 18538 * @file html-track-element-list.js 18539 */ 18540 18541/** 18542 * The current list of {@link HtmlTrackElement}s. 18543 */ 18544var HtmlTrackElementList = function () { 18545 18546 /** 18547 * Create an instance of this class. 18548 * 18549 * @param {HtmlTrackElement[]} [tracks=[]] 18550 * A list of `HtmlTrackElement` to instantiate the list with. 18551 */ 18552 function HtmlTrackElementList() { 18553 var trackElements = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; 18554 18555 _classCallCheck(this, HtmlTrackElementList); 18556 18557 var list = this; // eslint-disable-line 18558 18559 if (browser.IS_IE8) { 18560 list = _document2['default'].createElement('custom'); 18561 18562 for (var prop in HtmlTrackElementList.prototype) { 18563 if (prop !== 'constructor') { 18564 list[prop] = HtmlTrackElementList.prototype[prop]; 18565 } 18566 } 18567 } 18568 18569 list.trackElements_ = []; 18570 18571 /** 18572 * @member {number} length 18573 * The current number of `Track`s in the this Trackist. 18574 */ 18575 Object.defineProperty(list, 'length', { 18576 get: function get() { 18577 return this.trackElements_.length; 18578 } 18579 }); 18580 18581 for (var i = 0, length = trackElements.length; i < length; i++) { 18582 list.addTrackElement_(trackElements[i]); 18583 } 18584 18585 if (browser.IS_IE8) { 18586 return list; 18587 } 18588 } 18589 18590 /** 18591 * Add an {@link HtmlTrackElement} to the `HtmlTrackElementList` 18592 * 18593 * @param {HtmlTrackElement} trackElement 18594 * The track element to add to the list. 18595 * 18596 * @private 18597 */ 18598 18599 18600 HtmlTrackElementList.prototype.addTrackElement_ = function addTrackElement_(trackElement) { 18601 var index = this.trackElements_.length; 18602 18603 if (!('' + index in this)) { 18604 Object.defineProperty(this, index, { 18605 get: function get() { 18606 return this.trackElements_[index]; 18607 } 18608 }); 18609 } 18610
18611 // Do not add duplicate elements 18612 if (this.trackElements_.indexOf(trackElement) === -1) { 18613 this.trackElements_.push(trackElement); 18614 } 18615 }; 18616 18617 /** 18618 * Get an {@link HtmlTrackElement} from the `HtmlTrackElementList` given an 18619 * {@link TextTrack}. 18620 * 18621 * @param {TextTrack} track 18622 * The track associated with a track element. 18623 * 18624 * @return {HtmlTrackElement|undefined} 18625 * The track element that was found or undefined. 18626 * 18627 * @private 18628 */ 18629 18630 18631 HtmlTrackElementList.prototype.getTrackElementByTrack_ = function getTrackElementByTrack_(track) { 18632 var trackElement_ = void 0; 18633 18634 for (var i = 0, length = this.trackElements_.length; i < length; i++) { 18635 if (track === this.trackElements_[i].track) { 18636 trackElement_ = this.trackElements_[i]; 18637 18638 break; 18639 } 18640 } 18641 18642 return trackElement_; 18643 }; 18644 18645 /** 18646 * Remove a {@link HtmlTrackElement} from the `HtmlTrackElementList` 18647 * 18648 * @param {HtmlTrackElement} trackElement 18649 * The track element to remove from the list. 18650 * 18651 * @private 18652 */ 18653 18654 18655 HtmlTrackElementList.prototype.removeTrackElement_ = function removeTrackElement_(trackElement) { 18656 for (var i = 0, length = this.trackElements_.length; i < length; i++) { 18657 if (trackElement === this.trackElements_[i]) { 18658 this.trackElements_.splice(i, 1); 18659 18660 break; 18661 } 18662 } 18663 }; 18664 18665 return HtmlTrackElementList; 18666}(); 18667 18668exports['default'] = HtmlTrackElementList; 18669 18670},{"78":78,"94":94}],66:[function(_dereq_,module,exports){ 18671'use strict'; 18672 18673exports.__esModule = true; 18674 18675var _browser = _dereq_(78); 18676 18677var browser = _interopRequireWildcard(_browser); 18678 18679var _document = _dereq_(94); 18680 18681var _document2 = _interopRequireDefault(_document); 18682 18683var _eventTarget = _dereq_(42); 18684 18685var _eventTarget2 = _interopRequireDefault(_eventTarget); 18686 18687var _textTrack = _dereq_(72); 18688 18689var _textTrack2 = _interopRequireDefault(_textTrack); 18690 18691function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 18692 18693function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 18694 18695function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 18696 18697function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 18698 18699function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 18700 * @file html-track-element.js 18701 */ 18702 18703/** 18704 * @typedef {HTMLTrackElement~ReadyState} 18705 * @enum {number} 18706 */ 18707var NONE = 0; 18708var LOADING = 1; 18709var LOADED = 2; 18710var ERROR = 3; 18711 18712/** 18713 * A single track represented in the DOM. 18714 * 18715 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#htmltrackelement}
18716 * @extends EventTarget 18717 */ 18718 18719var HTMLTrackElement = function (_EventTarget) { 18720 _inherits(HTMLTrackElement, _EventTarget); 18721 18722 /** 18723 * Create an instance of this class. 18724 * 18725 * @param {Object} options={} 18726 * Object of option names and values 18727 * 18728 * @param {Tech} options.tech 18729 * A reference to the tech that owns this HTMLTrackElement. 18730 * 18731 * @param {TextTrack~Kind} [options.kind='subtitles'] 18732 * A valid text track kind. 18733 * 18734 * @param {TextTrack~Mode} [options.mode='disabled'] 18735 * A valid text track mode. 18736 * 18737 * @param {string} [options.id='vjs_track_' + Guid.newGUID()] 18738 * A unique id for this TextTrack. 18739 * 18740 * @param {string} [options.label=''] 18741 * The menu label for this track. 18742 * 18743 * @param {string} [options.language=''] 18744 * A valid two character language code. 18745 * 18746 * @param {string} [options.srclang=''] 18747 * A valid two character language code. An alternative, but deprioritized 18748 * vesion of `options.language` 18749 * 18750 * @param {string} [options.src] 18751 * A url to TextTrack cues. 18752 * 18753 * @param {boolean} [options.default] 18754 * If this track should default to on or off. 18755 */ 18756 function HTMLTrackElement() { 18757 var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 18758 18759 _classCallCheck(this, HTMLTrackElement); 18760 18761 var _this = _possibleConstructorReturn(this, _EventTarget.call(this)); 18762 18763 var readyState = void 0; 18764 var trackElement = _this; // eslint-disable-line 18765 18766 if (browser.IS_IE8) { 18767 trackElement = _document2['default'].createElement('custom'); 18768 18769 for (var prop in HTMLTrackElement.prototype) { 18770 if (prop !== 'constructor') { 18771 trackElement[prop] = HTMLTrackElement.prototype[prop]; 18772 } 18773 } 18774 } 18775 18776 var track = new _textTrack2['default'](options); 18777 18778 trackElement.kind = track.kind; 18779 trackElement.src = track.src; 18780 trackElement.srclang = track.language; 18781 trackElement.label = track.label; 18782 trackElement['default'] = track['default']; 18783 18784 /** 18785 * @member {HTMLTrackElement~ReadyState} readyState 18786 * The current ready state of the track element. 18787 */ 18788 Object.defineProperty(trackElement, 'readyState', { 18789 get: function get() { 18790 return readyState; 18791 } 18792 }); 18793 18794 /** 18795 * @member {TextTrack} track 18796 * The underlying TextTrack object. 18797 */ 18798 Object.defineProperty(trackElement, 'track', { 18799 get: function get() { 18800 return track; 18801 } 18802 }); 18803 18804 readyState = NONE; 18805 18806 /** 18807 * @listens TextTrack#loadeddata 18808 * @fires HTMLTrackElement#load 18809 */ 18810 track.addEventListener('loadeddata', function () { 18811 readyState = LOADED; 18812 18813 trackElement.trigger({ 18814 type: 'load', 18815 target: trackElement 18816 }); 18817 }); 18818 18819 if (browser.IS_IE8) { 18820 var _ret; 18821 18822 return _ret = trackElement, _possibleConstructorReturn(_this, _ret); 18823 } 18824 return _this; 18825 } 18826 18827 return HTMLTrackElement; 18828}(_eventTarget2['default']); 18829 18830HTMLTrackElement.prototype.allowedEvents_ = { 18831 load: 'load' 18832}; 18833 18834HTMLTrackElement.NONE = NONE; 18835HTMLTrackElement.LOADING = LOADING; 18836HTMLTrackElement.LOADED = LOADED; 18837HTMLTrackElement.ERROR = ERROR; 18838 18839exports['default'] = HTMLTrackElement; 18840 18841},{"42":42,"72":72,"78":78,"94":94}],67:[function(_dereq_,module,exports){ 18842'use strict'; 18843 18844exports.__esModule = true; 18845 18846var _browser = _dereq_(78); 18847 18848var browser = _interopRequireWildcard(_browser); 18849 18850var _document = _dereq_(94); 18851 18852var _document2 = _interopRequireDefault(_document); 18853 18854function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 18855 18856function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 18857 18858function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** 18859 * @file text-track-cue-list.js 18860 */ 18861 18862 18863/** 18864 * @typedef {Object} TextTrackCue 18865 * 18866 * @property {string} id 18867 * The unique id for this text track cue 18868 * 18869 * @property {number} startTime 18870 * The start time for this text track cue 18871 * 18872 * @property {number} endTime 18873 * The end time for this text track cue 18874 * 18875 * @property {boolean} pauseOnExit 18876 * Pause when the end time is reached if true. 18877 * 18878 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcue} 18879 */ 18880 18881/** 18882 * A List of TextTrackCues. 18883 * 18884 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcuelist} 18885 */ 18886var TextTrackCueList = function () { 18887 18888 /** 18889 * Create an instance of this class.. 18890 * 18891 * @param {Array} cues 18892 * A list of cues to be initialized with 18893 */ 18894 function TextTrackCueList(cues) { 18895 _classCallCheck(this, TextTrackCueList); 18896 18897 var list = this; // eslint-disable-line 18898 18899 if (browser.IS_IE8) { 18900 list = _document2['default'].createElement('custom'); 18901 18902 for (var prop in TextTrackCueList.prototype) { 18903 if (prop !== 'constructor') { 18904 list[prop] = TextTrackCueList.prototype[prop]; 18905 } 18906 } 18907 } 18908 18909 TextTrackCueList.prototype.setCues_.call(list, cues); 18910 18911 /** 18912 * @member {number} length 18913 * The current number of `TextTrackCue`s in the TextTrackCueList. 18914 */ 18915 Object.defineProperty(list, 'length', { 18916 get: function get() { 18917 return this.length_; 18918 } 18919 }); 18920 18921 if (browser.IS_IE8) { 18922 return list; 18923 } 18924 } 18925 18926 /** 18927 * A setter for cues in this list. Creates getters 18928 * an an index for the cues. 18929 * 18930 * @param {Array} cues 18931 * An array of cues to set 18932 * 18933 * @private 18934 */ 18935 18936 18937 TextTrackCueList.prototype.setCues_ = function setCues_(cues) { 18938 var oldLength = this.length || 0; 18939 var i = 0; 18940 var l = cues.length; 18941 18942 this.cues_ = cues; 18943 this.length_ = cues.length; 18944 18945 var defineProp = function defineProp(index) { 18946 if (!('' + index in this)) { 18947 Object.defineProperty(this, '' + index, { 18948 get: function get() { 18949 return this.cues_[index]; 18950 } 18951 }); 18952 } 18953 }; 18954 18955 if (oldLength < l) { 18956 i = oldLength; 18957 18958 for (; i < l; i++) { 18959 defineProp.call(this, i); 18960 } 18961 } 18962 }; 18963 18964 /** 18965 * Get a `TextTrackCue` that is currently in the `TextTrackCueList` by id. 18966 * 18967 * @param {string} id 18968 * The id of the cue that should be searched for. 18969 * 18970 * @return {TextTrackCue|null} 18971 * A single cue or null if none was found. 18972 */ 18973 18974 18975 TextTrackCueList.prototype.getCueById = function getCueById(id) { 18976 var result = null; 18977 18978 for (var i = 0, l = this.length; i < l; i++) { 18979 var cue = this[i]; 18980 18981 if (cue.id === id) { 18982 result = cue; 18983 break; 18984 } 18985 } 18986 18987 return result; 18988 }; 18989 18990 return TextTrackCueList; 18991}(); 18992 18993exports['default'] = TextTrackCueList; 18994 18995},{"78":78,"94":94}],68:[function(_dereq_,module,exports){ 18996'use strict'; 18997 18998exports.__esModule = true; 18999 19000var _component = _dereq_(5); 19001 19002var _component2 = _interopRequireDefault(_component); 19003 19004var _fn = _dereq_(83); 19005 19006var Fn = _interopRequireWildcard(_fn); 19007 19008var _window = _dereq_(95); 19009 19010var _window2 = _interopRequireDefault(_window); 19011 19012function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 19013 19014function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 19015 19016function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 19017 19018function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 19019 19020function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
19020onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 19021 * @file text-track-display.js 19022 */ 19023 19024 19025var darkGray = '#222'; 19026var lightGray = '#ccc'; 19027var fontMap = { 19028 monospace: 'monospace', 19029 sansSerif: 'sans-serif', 19030 serif: 'serif', 19031 monospaceSansSerif: '"Andale Mono", "Lucida Console", monospace', 19032 monospaceSerif: '"Courier New", monospace', 19033 proportionalSansSerif: 'sans-serif', 19034 proportionalSerif: 'serif', 19035 casual: '"Comic Sans MS", Impact, fantasy', 19036 script: '"Monotype Corsiva", cursive', 19037 smallcaps: '"Andale Mono", "Lucida Console", monospace, sans-serif' 19038}; 19039 19040/** 19041 * Construct an rgba color from a given hex color code. 19042 * 19043 * @param {number} color 19044 * Hex number for color, like #f0e. 19045 * 19046 * @param {number} opacity 19047 * Value for opacity, 0.0 - 1.0. 19048 * 19049 * @return {string} 19050 * The rgba color that was created, like 'rgba(255, 0, 0, 0.3)'. 19051 * 19052 * @private 19053 */ 19054function constructColor(color, opacity) { 19055 return 'rgba(' + 19056 // color looks like "#f0e" 19057 parseInt(color[1] + color[1], 16) + ',' + parseInt(color[2] + color[2], 16) + ',' + parseInt(color[3] + color[3], 16) + ',' + opacity + ')'; 19058} 19059 19060/** 19061 * Try to update the style of a DOM element. Some style changes will throw an error, 19062 * particularly in IE8. Those should be noops. 19063 * 19064 * @param {Element} el 19065 * The DOM element to be styled. 19066 * 19067 * @param {string} style 19068 * The CSS property on the element that should be styled. 19069 * 19070 * @param {string} rule 19071 * The style rule that should be applied to the property. 19072 */ 19073function tryUpdateStyle(el, style, rule) { 19074 try { 19075 el.style[style] = rule; 19076 } catch (e) { 19077 19078 // Satisfies linter. 19079 return; 19080 } 19081} 19082 19083/** 19084 * The component for displaying text track cues. 19085 * 19086 * @extends Component 19087 */ 19088 19089var TextTrackDisplay = function (_Component) { 19090 _inherits(TextTrackDisplay, _Component); 19091 19092 /** 19093 * Creates an instance of this class. 19094 * 19095 * @param {Player} player 19096 * The `Player` that this class should be attached to. 19097 * 19098 * @param {Object} [options] 19099 * The key/value store of player options. 19100 * 19101 * @param {Component~ReadyCallback} [ready] 19102 * The function to call when `TextTrackDisplay` is ready. 19103 */ 19104 function TextTrackDisplay(player, options, ready) { 19105 _classCallCheck(this, TextTrackDisplay); 19106 19107 var _this = _possibleConstructorReturn(this, _Component.call(this, player, options, ready)); 19108 19109 player.on('loadstart', Fn.bind(_this, _this.toggleDisplay)); 19110 player.on('texttrackchange', Fn.bind(_this, _this.updateDisplay)); 19111 19112 // This used to be called during player init, but was causing an error 19113 // if a track should show by default and the display hadn't loaded yet. 19114 // Should probably be moved to an external track loader when we support 19115 // tracks that don't need a display. 19116 player.ready(Fn.bind(_this, function () { 19117 if (player.tech_ && player.tech_.featuresNativeTextTracks) { 19118 this.hide(); 19119 return; 19120 } 19121 19122 player.on('fullscreenchange', Fn.bind(this, this.updateDisplay)); 19123 19124 var tracks = this.options_.playerOptions.tracks || []; 19125 19126 for (var i = 0; i < tracks.length; i++) { 19127 this.player_.addRemoteTextTrack(tracks[i], true); 19128 } 19129 19130 var modes = { captions: 1, subtitles: 1 }; 19131 var trackList = this.player_.textTracks(); 19132 var firstDesc = void 0; 19133 var firstCaptions = void 0; 19134 19135 if (trackList) { 19136 for (var _i = 0; _i < trackList.length; _i++) { 19137 var track = trackList[_i]; 19138 19139 if (track['default']) { 19140 if (track.kind === 'descriptions' && !firstDesc) { 19141 firstDesc = track; 19142 } else if (track.kind in modes && !firstCaptions) { 19143 firstCaptions = track; 19144 } 19145 } 19146 } 19147 19148 // We want to show the first default track but captions and subtitles 19149 // take precedence over descriptions. 19150 // So, display the first default captions or subtitles track 19151 // and otherwise the first default descriptions track. 19152 if (firstCaptions) { 19153 firstCaptions.mode = 'showing'; 19154 } else if (firstDesc) { 19155 firstDesc.mode = 'showing'; 19156 } 19157 } 19158 })); 19159 return _this; 19160 } 19161 19162 /** 19163 * Turn display of {@link TextTrack}'s from the current state into the other state. 19164 * There are only two states: 19165 * - 'shown' 19166 * - 'hidden' 19167 * 19168 * @listens Player#loadstart 19169 */ 19170 19171 19172 TextTrackDisplay.prototype.toggleDisplay = function toggleDisplay() { 19173 if (this.player_.tech_ && this.player_.tech_.featuresNativeTextTracks) { 19174 this.hide(); 19175 } else { 19176 this.show(); 19177 } 19178 }; 19179 19180 /** 19181 * Create the {@link Component}'s DOM element. 19182 * 19183 * @return {Element} 19184 * The element that was created. 19185 */ 19186 19187 19188 TextTrackDisplay.prototype.createEl = function createEl() { 19189 return _Component.prototype.createEl.call(this, 'div', { 19190 className: 'vjs-text-track-display' 19191 }, {
19192 'aria-live': 'off', 19193 'aria-atomic': 'true' 19194 }); 19195 }; 19196 19197 /** 19198 * Clear all displayed {@link TextTrack}s. 19199 */ 19200 19201 19202 TextTrackDisplay.prototype.clearDisplay = function clearDisplay() { 19203 if (typeof _window2['default'].WebVTT === 'function') { 19204 _window2['default'].WebVTT.processCues(_window2['default'], [], this.el_); 19205 } 19206 }; 19207 19208 /** 19209 * Update the displayed TextTrack when a either a {@link Player#texttrackchange} or 19210 * a {@link Player#fullscreenchange} is fired. 19211 * 19212 * @listens Player#texttrackchange 19213 * @listens Player#fullscreenchange 19214 */ 19215 19216 19217 TextTrackDisplay.prototype.updateDisplay = function updateDisplay() { 19218 var tracks = this.player_.textTracks(); 19219 19220 this.clearDisplay(); 19221 19222 if (!tracks) { 19223 return; 19224 } 19225 19226 // Track display prioritization model: if multiple tracks are 'showing', 19227 // display the first 'subtitles' or 'captions' track which is 'showing', 19228 // otherwise display the first 'descriptions' track which is 'showing' 19229 19230 var descriptionsTrack = null; 19231 var captionsSubtitlesTrack = null; 19232 19233 var i = tracks.length; 19234 19235 while (i--) { 19236 var track = tracks[i]; 19237 19238 if (track.mode === 'showing') { 19239 if (track.kind === 'descriptions') { 19240 descriptionsTrack = track; 19241 } else { 19242 captionsSubtitlesTrack = track; 19243 } 19244 } 19245 } 19246 19247 if (captionsSubtitlesTrack) { 19248 if (this.getAttribute('aria-live') !== 'off') { 19249 this.setAttribute('aria-live', 'off'); 19250 } 19251 this.updateForTrack(captionsSubtitlesTrack); 19252 } else if (descriptionsTrack) { 19253 if (this.getAttribute('aria-live') !== 'assertive') { 19254 this.setAttribute('aria-live', 'assertive'); 19255 } 19256 this.updateForTrack(descriptionsTrack); 19257 } 19258 }; 19259 19260 /** 19261 * Add an {@link Texttrack} to to the {@link Tech}s {@link TextTrackList}. 19262 * 19263 * @param {TextTrack} track 19264 * Text track object to be added to the list. 19265 */ 19266 19267 19268 TextTrackDisplay.prototype.updateForTrack = function updateForTrack(track) { 19269 if (typeof _window2['default'].WebVTT !== 'function' || !track.activeCues) { 19270 return; 19271 } 19272 19273 var overrides = this.player_.textTrackSettings.getValues(); 19274 var cues = []; 19275 19276 for (var _i2 = 0; _i2 < track.activeCues.length; _i2++) { 19277 cues.push(track.activeCues[_i2]); 19278 } 19279 19280 _window2['default'].WebVTT.processCues(_window2['default'], cues, this.el_); 19281 19282 var i = cues.length; 19283 19284 while (i--) { 19285 var cue = cues[i]; 19286 19287 if (!cue) { 19288 continue; 19289 } 19290 19291 var cueDiv = cue.displayState; 19292 19293 if (overrides.color) { 19294 cueDiv.firstChild.style.color = overrides.color; 19295 } 19296 if (overrides.textOpacity) { 19297 tryUpdateStyle(cueDiv.firstChild, 'color', constructColor(overrides.color || '#fff', overrides.textOpacity)); 19298 } 19299 if (overrides.backgroundColor) { 19300 cueDiv.firstChild.style.backgroundColor = overrides.backgroundColor; 19301 } 19302 if (overrides.backgroundOpacity) { 19303 tryUpdateStyle(cueDiv.firstChild, 'backgroundColor', constructColor(overrides.backgroundColor || '#000', overrides.backgroundOpacity)); 19304 } 19305 if (overrides.windowColor) { 19306 if (overrides.windowOpacity) { 19307 tryUpdateStyle(cueDiv, 'backgroundColor', constructColor(overrides.windowColor, overrides.windowOpacity)); 19308 } else { 19309 cueDiv.style.backgroundColor = overrides.windowColor; 19310 } 19311 } 19312 if (overrides.edgeStyle) { 19313 if (overrides.edgeStyle === 'dropshadow') { 19314 cueDiv.firstChild.style.textShadow = '2px 2px 3px ' + darkGray + ', 2px 2px 4px ' + darkGray + ', 2px 2px 5px ' + darkGray; 19315 } else if (overrides.edgeStyle === 'raised') { 19316 cueDiv.firstChild.style.textShadow = '1px 1px ' + darkGray + ', 2px 2px ' + darkGray + ', 3px 3px ' + darkGray; 19317 } else if (overrides.edgeStyle === 'depressed') { 19318 cueDiv.firstChild.style.textShadow = '1px 1px ' + lightGray + ', 0 1px ' + lightGray + ', -1px -1px ' + darkGray + ', 0 -1px ' + darkGray; 19319 } else if (overrides.edgeStyle === 'uniform') { 19320 cueDiv.firstChild.style.textShadow = '0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray; 19321 } 19322 } 19323 if (overrides.fontPercent && overrides.fontPercent !== 1) { 19324 var fontSize = _window2['default'].parseFloat(cueDiv.style.fontSize); 19325 19326 cueDiv.style.fontSize = fontSize * overrides.fontPercent + 'px'; 19327 cueDiv.style.height = 'auto'; 19328 cueDiv.style.top = 'auto'; 19329 cueDiv.style.bottom = '2px'; 19330 } 19331 if (overrides.fontFamily && overrides.fontFamily !== 'default') { 19332 if (overrides.fontFamily === 'small-caps') { 19333 cueDiv.firstChild.style.fontVariant = 'small-caps'; 19334 } else { 19335 cueDiv.firstChild.style.fontFamily = fontMap[overrides.fontFamily]; 19336 } 19337 } 19338 } 19339 }; 19340 19341 return TextTrackDisplay; 19342}(_component2['default']); 19343 19344_component2['default'].registerComponent('TextTrackDisplay', TextTrackDisplay); 19345exports['default'] = TextTrackDisplay; 19346 19347},{"5":5,"83":83,"95":95}],69:[function(_dereq_,module,exports){ 19348'use strict'; 19349 19350exports.__esModule = true; 19351/** 19352 * @file text-track-list-converter.js Utilities for capturing text track state and 19353 * re-creating tracks based on a capture. 19354 * 19355 * @module text-track-list-converter 19356 */ 19357 19358/**
19359 * Examine a single {@link TextTrack} and return a JSON-compatible javascript object that 19360 * represents the {@link TextTrack}'s state. 19361 * 19362 * @param {TextTrack} track 19363 * The text track to query. 19364 * 19365 * @return {Object} 19366 * A serializable javascript representation of the TextTrack. 19367 * @private 19368 */ 19369var trackToJson_ = function trackToJson_(track) { 19370 var ret = ['kind', 'label', 'language', 'id', 'inBandMetadataTrackDispatchType', 'mode', 'src'].reduce(function (acc, prop, i) { 19371 19372 if (track[prop]) { 19373 acc[prop] = track[prop]; 19374 } 19375 19376 return acc; 19377 }, { 19378 cues: track.cues && Array.prototype.map.call(track.cues, function (cue) { 19379 return { 19380 startTime: cue.startTime, 19381 endTime: cue.endTime, 19382 text: cue.text, 19383 id: cue.id 19384 }; 19385 }) 19386 }); 19387 19388 return ret; 19389}; 19390 19391/** 19392 * Examine a {@link Tech} and return a JSON-compatible javascript array that represents the 19393 * state of all {@link TextTrack}s currently configured. The return array is compatible with 19394 * {@link text-track-list-converter:jsonToTextTracks}. 19395 * 19396 * @param {Tech} tech 19397 * The tech object to query 19398 * 19399 * @return {Array} 19400 * A serializable javascript representation of the {@link Tech}s 19401 * {@link TextTrackList}. 19402 */ 19403var textTracksToJson = function textTracksToJson(tech) { 19404 19405 var trackEls = tech.$$('track'); 19406 19407 var trackObjs = Array.prototype.map.call(trackEls, function (t) { 19408 return t.track; 19409 }); 19410 var tracks = Array.prototype.map.call(trackEls, function (trackEl) { 19411 var json = trackToJson_(trackEl.track); 19412 19413 if (trackEl.src) { 19414 json.src = trackEl.src; 19415 } 19416 return json; 19417 }); 19418 19419 return tracks.concat(Array.prototype.filter.call(tech.textTracks(), function (track) { 19420 return trackObjs.indexOf(track) === -1; 19421 }).map(trackToJson_)); 19422}; 19423 19424/** 19425 * Create a set of remote {@link TextTrack}s on a {@link Tech} based on an array of javascript 19426 * object {@link TextTrack} representations. 19427 * 19428 * @param {Array} json 19429 * An array of `TextTrack` representation objects, like those that would be 19430 * produced by `textTracksToJson`. 19431 * 19432 * @param {Tech} tech 19433 * The `Tech` to create the `TextTrack`s on. 19434 */ 19435var jsonToTextTracks = function jsonToTextTracks(json, tech) { 19436 json.forEach(function (track) { 19437 var addedTrack = tech.addRemoteTextTrack(track).track; 19438 19439 if (!track.src && track.cues) { 19440 track.cues.forEach(function (cue) { 19441 return addedTrack.addCue(cue); 19442 }); 19443 } 19444 }); 19445 19446 return tech.textTracks(); 19447}; 19448 19449exports['default'] = { textTracksToJson: textTracksToJson, jsonToTextTracks: jsonToTextTracks, trackToJson_: trackToJson_ }; 19450 19451},{}],70:[function(_dereq_,module,exports){ 19452'use strict'; 19453 19454exports.__esModule = true; 19455 19456var _trackList = _dereq_(74); 19457 19458var _trackList2 = _interopRequireDefault(_trackList); 19459 19460var _fn = _dereq_(83); 19461 19462var Fn = _interopRequireWildcard(_fn); 19463 19464var _browser = _dereq_(78); 19465 19466var browser = _interopRequireWildcard(_browser); 19467 19468var _document = _dereq_(94); 19469 19470var _document2 = _interopRequireDefault(_document); 19471 19472function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 19473 19474function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 19475 19476function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 19477 19478function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 19479 19480function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 19481 * @file text-track-list.js 19482 */ 19483 19484 19485/** 19486 * The current list of {@link TextTrack} for a media file. 19487 * 19488 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttracklist} 19489 * @extends TrackList 19490 */ 19491var TextTrackList = function (_TrackList) { 19492 _inherits(TextTrackList, _TrackList); 19493 19494 /** 19495 * Create an instance of this class. 19496 * 19497 * @param {TextTrack[]} [tracks=[]] 19498 * A list of `TextTrack` to instantiate the list with. 19499 */ 19500 function TextTrackList() { 19501 var _this, _ret; 19502 19503 var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; 19504 19505 _classCallCheck(this, TextTrackList); 19506 19507 var list = void 0; 19508 19509 // IE8 forces us to implement inheritance ourselves 19510 // as it does not support Object.defineProperty properly 19511 if (browser.IS_IE8) { 19512 list = _document2['default'].createElement('custom'); 19513 for (var prop in _trackList2['default'].prototype) { 19514 if (prop !== 'constructor') { 19515 list[prop] = _trackList2['default'].prototype[prop]; 19516 } 19517 } 19518 for (var _prop in TextTrackList.prototype) { 19519 if (_prop !== 'constructor') { 19520 list[_prop] = TextTrackList.prototype[_prop]; 19521 } 19522 } 19523 } 19524
19525 list = (_this = _possibleConstructorReturn(this, _TrackList.call(this, tracks, list)), _this); 19526 return _ret = list, _possibleConstructorReturn(_this, _ret); 19527 } 19528 19529 /** 19530 * Add a {@link TextTrack} to the `TextTrackList` 19531 * 19532 * @param {TextTrack} track 19533 * The text track to add to the list. 19534 * 19535 * @fires TrackList#addtrack 19536 * @private 19537 */ 19538 19539 19540 TextTrackList.prototype.addTrack_ = function addTrack_(track) { 19541 _TrackList.prototype.addTrack_.call(this, track); 19542 19543 /** 19544 * @listens TextTrack#modechange 19545 * @fires TrackList#change 19546 */ 19547 track.addEventListener('modechange', Fn.bind(this, function () { 19548 this.trigger('change'); 19549 })); 19550 }; 19551 19552 return TextTrackList; 19553}(_trackList2['default']); 19554 19555exports['default'] = TextTrackList; 19556 19557},{"74":74,"78":78,"83":83,"94":94}],71:[function(_dereq_,module,exports){ 19558'use strict'; 19559 19560exports.__esModule = true; 19561 19562var _window = _dereq_(95); 19563 19564var _window2 = _interopRequireDefault(_window); 19565 19566var _component = _dereq_(5); 19567 19568var _component2 = _interopRequireDefault(_component); 19569 19570var _dom = _dereq_(81); 19571 19572var _fn = _dereq_(83); 19573 19574var Fn = _interopRequireWildcard(_fn); 19575 19576var _obj = _dereq_(88); 19577 19578var Obj = _interopRequireWildcard(_obj); 19579 19580var _log = _dereq_(86); 19581 19582var _log2 = _interopRequireDefault(_log); 19583 19584function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 19585 19586function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 19587 19588function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 19589 19590function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 19591 19592function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 19593 * @file text-track-settings.js 19594 */ 19595 19596 19597var LOCAL_STORAGE_KEY = 'vjs-text-track-settings'; 19598 19599var COLOR_BLACK = ['#000', 'Black']; 19600var COLOR_BLUE = ['#00F', 'Blue']; 19601var COLOR_CYAN = ['#0FF', 'Cyan']; 19602var COLOR_GREEN = ['#0F0', 'Green']; 19603var COLOR_MAGENTA = ['#F0F', 'Magenta']; 19604var COLOR_RED = ['#F00', 'Red']; 19605var COLOR_WHITE = ['#FFF', 'White']; 19606var COLOR_YELLOW = ['#FF0', 'Yellow']; 19607 19608var OPACITY_OPAQUE = ['1', 'Opaque']; 19609var OPACITY_SEMI = ['0.5', 'Semi-Transparent']; 19610var OPACITY_TRANS = ['0', 'Transparent']; 19611 19612// Configuration for the various <select> elements in the DOM of this component. 19613// 19614// Possible keys include: 19615// 19616// `default`: 19617// The default option index. Only needs to be provided if not zero. 19618// `parser`: 19619// A function which is used to parse the value from the selected option in 19620// a customized way. 19621// `selector`: 19622// The selector used to find the associated <select> element. 19623var selectConfigs = { 19624 backgroundColor: { 19625 selector: '.vjs-bg-color > select', 19626 id: 'captions-background-color-%s', 19627 label: 'Color', 19628 options: [COLOR_BLACK, COLOR_WHITE, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_MAGENTA, COLOR_CYAN] 19629 }, 19630 19631 backgroundOpacity: { 19632 selector: '.vjs-bg-opacity > select', 19633 id: 'captions-background-opacity-%s', 19634 label: 'Transparency', 19635 options: [OPACITY_OPAQUE, OPACITY_SEMI, OPACITY_TRANS] 19636 }, 19637 19638 color: { 19639 selector: '.vjs-fg-color > select', 19640 id: 'captions-foreground-color-%s', 19641 label: 'Color', 19642 options: [COLOR_WHITE, COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_MAGENTA, COLOR_CYAN] 19643 }, 19644 19645 edgeStyle: { 19646 selector: '.vjs-edge-style > select', 19647 id: '%s', 19648 label: 'Text Edge Style', 19649 options: [['none', 'None'], ['raised', 'Raised'], ['depressed', 'Depressed'], ['uniform', 'Uniform'], ['dropshadow', 'Dropshadow']] 19650 }, 19651 19652 fontFamily: { 19653 selector: '.vjs-font-family > select', 19654 id: 'captions-font-family-%s', 19655 label: 'Font Family', 19656 options: [['proportionalSansSerif', 'Proportional Sans-Serif'], ['monospaceSansSerif', 'Monospace Sans-Serif'], ['proportionalSerif', 'Proportional Serif'], ['monospaceSerif', 'Monospace Serif'], ['casual', 'Casual'], ['script', 'Script'], ['small-caps', '
vendor: 4,800 bytes, lines 19656-19845
19656Small Caps']] 19657 }, 19658 19659 fontPercent: { 19660 selector: '.vjs-font-percent > select', 19661 id: 'captions-font-size-%s', 19662 label: 'Font Size', 19663 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%']], 19664 'default': 2, 19665 parser: function parser(v) { 19666 return v === '1.00' ? null : Number(v); 19667 } 19668 }, 19669 19670 textOpacity: { 19671 selector: '.vjs-text-opacity > select', 19672 id: 'captions-foreground-opacity-%s', 19673 label: 'Transparency', 19674 options: [OPACITY_OPAQUE, OPACITY_SEMI] 19675 }, 19676 19677 // Options for this object are defined below. 19678 windowColor: { 19679 selector: '.vjs-window-color > select', 19680 id: 'captions-window-color-%s', 19681 label: 'Color' 19682 }, 19683 19684 // Options for this object are defined below. 19685 windowOpacity: { 19686 selector: '.vjs-window-opacity > select', 19687 id: 'captions-window-opacity-%s', 19688 label: 'Transparency', 19689 options: [OPACITY_TRANS, OPACITY_SEMI, OPACITY_OPAQUE] 19690 } 19691}; 19692 19693selectConfigs.windowColor.options = selectConfigs.backgroundColor.options; 19694 19695/** 19696 * Get the actual value of an option. 19697 * 19698 * @param {string} value 19699 * The value to get 19700 * 19701 * @param {Function} [parser] 19702 * Optional function to adjust the value. 19703 * 19704 * @return {Mixed} 19705 * - Will be `undefined` if no value exists 19706 * - Will be `undefined` if the given value is "none". 19707 * - Will be the actual value otherwise. 19708 * 19709 * @private 19710 */ 19711function parseOptionValue(value, parser) { 19712 if (parser) { 19713 value = parser(value); 19714 } 19715 19716 if (value && value !== 'none') { 19717 return value; 19718 } 19719} 19720 19721/** 19722 * Gets the value of the selected <option> element within a <select> element. 19723 * 19724 * @param {Element} el 19725 * the element to look in 19726 * 19727 * @param {Function} [parser] 19728 * Optional function to adjust the value. 19729 * 19730 * @return {Mixed} 19731 * - Will be `undefined` if no value exists 19732 * - Will be `undefined` if the given value is "none". 19733 * - Will be the actual value otherwise. 19734 * 19735 * @private 19736 */ 19737function getSelectedOptionValue(el, parser) { 19738 var value = el.options[el.options.selectedIndex].value; 19739 19740 return parseOptionValue(value, parser); 19741} 19742 19743/** 19744 * Sets the selected <option> element within a <select> element based on a 19745 * given value. 19746 * 19747 * @param {Element} el 19748 * The element to look in. 19749 * 19750 * @param {string} value 19751 * the property to look on. 19752 * 19753 * @param {Function} [parser] 19754 * Optional function to adjust the value before comparing. 19755 * 19756 * @private 19757 */ 19758function setSelectedOption(el, value, parser) { 19759 if (!value) { 19760 return; 19761 } 19762 19763 for (var i = 0; i < el.options.length; i++) { 19764 if (parseOptionValue(el.options[i].value, parser) === value) { 19765 el.selectedIndex = i; 19766 break; 19767 } 19768 } 19769} 19770 19771/** 19772 * Manipulate Text Tracks settings. 19773 * 19774 * @extends Component 19775 */ 19776 19777var TextTrackSettings = function (_Component) { 19778 _inherits(TextTrackSettings, _Component); 19779 19780 /** 19781 * Creates an instance of this class. 19782 * 19783 * @param {Player} player 19784 * The `Player` that this class should be attached to. 19785 * 19786 * @param {Object} [options] 19787 * The key/value store of player options. 19788 */ 19789 function TextTrackSettings(player, options) { 19790 _classCallCheck(this, TextTrackSettings); 19791 19792 var _this = _possibleConstructorReturn(this, _Component.call(this, player, options)); 19793 19794 _this.setDefaults(); 19795 _this.hide(); 19796 19797 _this.updateDisplay = Fn.bind(_this, _this.updateDisplay); 19798 19799 // Grab `persistTextTrackSettings` from the player options if not passed in child options 19800 if (options.persistTextTrackSettings === undefined) { 19801 _this.options_.persistTextTrackSettings = _this.options_.playerOptions.persistTextTrackSettings; 19802 } 19803 19804 _this.on(_this.$('.vjs-done-button'), 'click', function () { 19805 _this.saveSettings(); 19806 _this.hide(); 19807 }); 19808 19809 _this.on(_this.$('.vjs-default-button'), 'click', function () { 19810 _this.setDefaults(); 19811 _this.updateDisplay(); 19812 }); 19813 19814 Obj.each(selectConfigs, function (config) { 19815 _this.on(_this.$(config.selector), 'change', _this.updateDisplay); 19816 }); 19817 19818 if (_this.options_.persistTextTrackSettings) { 19819 _this.restoreSettings(); 19820 } 19821 return _this; 19822 } 19823 19824 /** 19825 * Create a <select> element with configured options. 19826 * 19827 * @param {string} key 19828 * Configuration key to use during creation. 19829 * 19830 * @return {Element} 19831 * The DOM element that gets created. 19832 * @private 19833 */ 19834 19835 19836 TextTrackSettings.prototype.createElSelect_ = function createElSelect_(key) { 19837 var _this2 = this; 19838 19839 var config = selectConfigs[key]; 19840 var id = config.id.replace('%s', this.id_); 19841 19842 return [(0, _dom.createEl)('label', { 19843 className: 'vjs-label', 19844 textContent: config.label 19845 }, {
19846 'for': id 19847 }), (0, _dom.createEl)('select', { id: id }, undefined, config.options.map(function (o) { 19848 return (0, _dom.createEl)('option', { 19849 textContent: _this2.localize(o[1]), 19850 value: o[0] 19851 }); 19852 }))]; 19853 }; 19854 19855 /** 19856 * Create foreground color element for the component 19857 * 19858 * @return {Element} 19859 * The element that was created. 19860 * 19861 * @private 19862 */ 19863 19864 19865 TextTrackSettings.prototype.createElFgColor_ = function createElFgColor_() { 19866 var legend = (0, _dom.createEl)('legend', { 19867 textContent: this.localize('Text') 19868 }); 19869 19870 var select = this.createElSelect_('color'); 19871 19872 var opacity = (0, _dom.createEl)('span', { 19873 className: 'vjs-text-opacity vjs-opacity' 19874 }, undefined, this.createElSelect_('textOpacity')); 19875 19876 return (0, _dom.createEl)('fieldset', { 19877 className: 'vjs-fg-color vjs-tracksetting' 19878 }, undefined, [legend].concat(select, opacity)); 19879 }; 19880 19881 /** 19882 * Create background color element for the component 19883 * 19884 * @return {Element} 19885 * The element that was created 19886 * 19887 * @private 19888 */ 19889 19890 19891 TextTrackSettings.prototype.createElBgColor_ = function createElBgColor_() { 19892 var legend = (0, _dom.createEl)('legend', { 19893 textContent: this.localize('Background') 19894 }); 19895 19896 var select = this.createElSelect_('backgroundColor'); 19897 19898 var opacity = (0, _dom.createEl)('span', { 19899 className: 'vjs-bg-opacity vjs-opacity' 19900 }, undefined, this.createElSelect_('backgroundOpacity')); 19901 19902 return (0, _dom.createEl)('fieldset', { 19903 className: 'vjs-bg-color vjs-tracksetting' 19904 }, undefined, [legend].concat(select, opacity)); 19905 }; 19906 19907 /** 19908 * Create window color element for the component 19909 * 19910 * @return {Element} 19911 * The element that was created 19912 * 19913 * @private 19914 */ 19915 19916 19917 TextTrackSettings.prototype.createElWinColor_ = function createElWinColor_() { 19918 var legend = (0, _dom.createEl)('legend', { 19919 textContent: this.localize('Window') 19920 }); 19921 19922 var select = this.createElSelect_('windowColor'); 19923 19924 var opacity = (0, _dom.createEl)('span', { 19925 className: 'vjs-window-opacity vjs-opacity' 19926 }, undefined, this.createElSelect_('windowOpacity')); 19927 19928 return (0, _dom.createEl)('fieldset', { 19929 className: 'vjs-window-color vjs-tracksetting' 19930 }, undefined, [legend].concat(select, opacity)); 19931 }; 19932 19933 /** 19934 * Create color elements for the component 19935 * 19936 * @return {Element} 19937 * The element that was created 19938 * 19939 * @private 19940 */ 19941 19942 19943 TextTrackSettings.prototype.createElColors_ = function createElColors_() { 19944 return (0, _dom.createEl)('div', { 19945 className: 'vjs-tracksettings-colors' 19946 }, undefined, [this.createElFgColor_(), this.createElBgColor_(), this.createElWinColor_()]); 19947 }; 19948 19949 /** 19950 * Create font elements for the component 19951 * 19952 * @return {Element} 19953 * The element that was created. 19954 * 19955 * @private 19956 */ 19957 19958 19959 TextTrackSettings.prototype.createElFont_ = function createElFont_() { 19960 var fontPercent = (0, _dom.createEl)('div', { 19961 className: 'vjs-font-percent vjs-tracksetting' 19962 }, undefined, this.createElSelect_('fontPercent')); 19963 19964 var edgeStyle = (0, _dom.createEl)('div', { 19965 className: 'vjs-edge-style vjs-tracksetting' 19966 }, undefined, this.createElSelect_('edgeStyle')); 19967 19968 var fontFamily = (0, _dom.createEl)('div', { 19969 className: 'vjs-font-family vjs-tracksetting' 19970 }, undefined, this.createElSelect_('fontFamily')); 19971 19972 return (0, _dom.createEl)('div', { 19973 className: 'vjs-tracksettings-font' 19974 }, undefined, [fontPercent, edgeStyle, fontFamily]); 19975 }; 19976 19977 /** 19978 * Create controls for the component 19979 * 19980 * @return {Element} 19981 * The element that was created. 19982 * 19983 * @private 19984 */ 19985 19986 19987 TextTrackSettings.prototype.createElControls_ = function createElControls_() { 19988 var defaultsButton = (0, _dom.createEl)('button', { 19989 className: 'vjs-default-button', 19990 textContent: this.localize('Defaults') 19991 }); 19992 19993 var doneButton = (0, _dom.createEl)('button', { 19994 className: 'vjs-done-button', 19995 textContent: 'Done' 19996 }); 19997 19998 return (0, _dom.createEl)('div', { 19999 className: 'vjs-tracksettings-controls' 20000 }, undefined, [defaultsButton, doneButton]); 20001 }; 20002 20003 /** 20004 * Create the component's DOM element 20005 * 20006 * @return {Element} 20007 * The element that was created. 20008 */ 20009 20010 20011 TextTrackSettings.prototype.createEl = function createEl() { 20012 var settings = (0, _dom.createEl)('div', { 20013 className: 'vjs-tracksettings' 20014 }, undefined, [this.createElColors_(), this.createElFont_(), this.createElControls_()]); 20015 20016 var heading = (0, _dom.createEl)('div', { 20017 className: 'vjs-control-text', 20018 id: 'TTsettingsDialogLabel-' + this.id_, 20019 textContent: 'Caption Settings Dialog' 20020 }, {
20021 'aria-level': '1', 20022 'role': 'heading' 20023 }); 20024 20025 var description = (0, _dom.createEl)('div', { 20026 className: 'vjs-control-text', 20027 id: 'TTsettingsDialogDescription-' + this.id_, 20028 textContent: 'Beginning of dialog window. Escape will cancel and close the window.' 20029 }); 20030 20031 var doc = (0, _dom.createEl)('div', undefined, { 20032 role: 'document' 20033 }, [heading, description, settings]); 20034 20035 return (0, _dom.createEl)('div', { 20036 className: 'vjs-caption-settings vjs-modal-overlay', 20037 tabIndex: -1 20038 }, { 20039 'role': 'dialog', 20040 'aria-labelledby': heading.id, 20041 'aria-describedby': description.id 20042 }, doc); 20043 }; 20044 20045 /** 20046 * Gets an object of text track settings (or null). 20047 * 20048 * @return {Object} 20049 * An object with config values parsed from the DOM or localStorage. 20050 */ 20051 20052 20053 TextTrackSettings.prototype.getValues = function getValues() { 20054 var _this3 = this; 20055 20056 return Obj.reduce(selectConfigs, function (accum, config, key) { 20057 var value = getSelectedOptionValue(_this3.$(config.selector), config.parser); 20058 20059 if (value !== undefined) { 20060 accum[key] = value; 20061 } 20062 20063 return accum; 20064 }, {}); 20065 }; 20066 20067 /** 20068 * Sets text track settings from an object of values. 20069 * 20070 * @param {Object} values 20071 * An object with config values parsed from the DOM or localStorage. 20072 */ 20073 20074 20075 TextTrackSettings.prototype.setValues = function setValues(values) { 20076 var _this4 = this; 20077 20078 Obj.each(selectConfigs, function (config, key) { 20079 setSelectedOption(_this4.$(config.selector), values[key], config.parser); 20080 }); 20081 }; 20082 20083 /** 20084 * Sets all <select> elements to their default values. 20085 */ 20086 20087 20088 TextTrackSettings.prototype.setDefaults = function setDefaults() { 20089 var _this5 = this; 20090 20091 Obj.each(selectConfigs, function (config) { 20092 var index = config.hasOwnProperty('default') ? config['default'] : 0; 20093 20094 _this5.$(config.selector).selectedIndex = index; 20095 }); 20096 }; 20097 20098 /** 20099 * Restore texttrack settings from localStorage 20100 */ 20101 20102 20103 TextTrackSettings.prototype.restoreSettings = function restoreSettings() { 20104 var values = void 0; 20105 20106 try { 20107 values = JSON.parse(_window2['default'].localStorage.getItem(LOCAL_STORAGE_KEY)); 20108 } catch (err) { 20109 _log2['default'].warn(err); 20110 } 20111 20112 if (values) { 20113 this.setValues(values); 20114 } 20115 }; 20116 20117 /** 20118 * Save text track settings to localStorage 20119 */ 20120 20121 20122 TextTrackSettings.prototype.saveSettings = function saveSettings() { 20123 if (!this.options_.persistTextTrackSettings) { 20124 return; 20125 } 20126 20127 var values = this.getValues(); 20128 20129 try { 20130 if (Object.keys(values).length) { 20131 _window2['default'].localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(values)); 20132 } else { 20133 _window2['default'].localStorage.removeItem(LOCAL_STORAGE_KEY); 20134 } 20135 } catch (err) { 20136 _log2['default'].warn(err); 20137 } 20138 }; 20139 20140 /** 20141 * Update display of text track settings 20142 */ 20143 20144 20145 TextTrackSettings.prototype.updateDisplay = function updateDisplay() { 20146 var ttDisplay = this.player_.getChild('textTrackDisplay'); 20147 20148 if (ttDisplay) { 20149 ttDisplay.updateDisplay(); 20150 } 20151 }; 20152 20153 return TextTrackSettings; 20154}(_component2['default']); 20155 20156_component2['default'].registerComponent('TextTrackSettings', TextTrackSettings); 20157 20158exports['default'] = TextTrackSettings; 20159 20160},{"5":5,"81":81,"83":83,"86":86,"88":88,"95":95}],72:[function(_dereq_,module,exports){ 20161'use strict'; 20162 20163exports.__esModule = true; 20164 20165var _textTrackCueList = _dereq_(67); 20166 20167var _textTrackCueList2 = _interopRequireDefault(_textTrackCueList); 20168 20169var _fn = _dereq_(83); 20170 20171var Fn = _interopRequireWildcard(_fn); 20172 20173var _trackEnums = _dereq_(73); 20174 20175var _log = _dereq_(86); 20176 20177var _log2 = _interopRequireDefault(_log); 20178 20179var _window = _dereq_(95); 20180 20181var _window2 = _interopRequireDefault(_window); 20182 20183var _track = _dereq_(75); 20184 20185var _track2 = _interopRequireDefault(_track); 20186 20187var _url = _dereq_(92); 20188 20189var _xhr = _dereq_(104); 20190 20191var _xhr2 = _interopRequireDefault(_xhr); 20192 20193var _mergeOptions = _dereq_(87); 20194 20195var _mergeOptions2 = _interopRequireDefault(_mergeOptions); 20196 20197var _browser = _dereq_(78); 20198 20199var browser = _interopRequireWildcard(_browser); 20200 20201function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 20202 20203function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 20204 20205function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 20206 20207function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 20208 20209function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
20209onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 20210 * @file text-track.js 20211 */ 20212 20213 20214/** 20215 * Takes a webvtt file contents and parses it into cues 20216 * 20217 * @param {string} srcContent 20218 * webVTT file contents 20219 * 20220 * @param {TextTrack} track 20221 * TextTrack to add cues to. Cues come from the srcContent. 20222 * 20223 * @private 20224 */ 20225var parseCues = function parseCues(srcContent, track) { 20226 var parser = new _window2['default'].WebVTT.Parser(_window2['default'], _window2['default'].vttjs, _window2['default'].WebVTT.StringDecoder()); 20227 var errors = []; 20228 20229 parser.oncue = function (cue) { 20230 track.addCue(cue); 20231 }; 20232 20233 parser.onparsingerror = function (error) { 20234 errors.push(error); 20235 }; 20236 20237 parser.onflush = function () { 20238 track.trigger({ 20239 type: 'loadeddata', 20240 target: track 20241 }); 20242 }; 20243 20244 parser.parse(srcContent); 20245 if (errors.length > 0) { 20246 if (_window2['default'].console && _window2['default'].console.groupCollapsed) { 20247 _window2['default'].console.groupCollapsed('Text Track parsing errors for ' + track.src); 20248 } 20249 errors.forEach(function (error) { 20250 return _log2['default'].error(error); 20251 }); 20252 if (_window2['default'].console && _window2['default'].console.groupEnd) { 20253 _window2['default'].console.groupEnd(); 20254 } 20255 } 20256 20257 parser.flush(); 20258}; 20259 20260/** 20261 * Load a `TextTrack` from a specifed url. 20262 * 20263 * @param {string} src 20264 * Url to load track from. 20265 * 20266 * @param {TextTrack} track 20267 * Track to add cues to. Comes from the content at the end of `url`. 20268 * 20269 * @private 20270 */ 20271var loadTrack = function loadTrack(src, track) { 20272 var opts = { 20273 uri: src 20274 }; 20275 var crossOrigin = (0, _url.isCrossOrigin)(src); 20276 20277 if (crossOrigin) { 20278 opts.cors = crossOrigin; 20279 } 20280 20281 (0, _xhr2['default'])(opts, Fn.bind(this, function (err, response, responseBody) { 20282 if (err) { 20283 return _log2['default'].error(err, response); 20284 } 20285 20286 track.loaded_ = true; 20287 20288 // Make sure that vttjs has loaded, otherwise, wait till it finished loading 20289 // NOTE: this is only used for the alt/video.novtt.js build 20290 if (typeof _window2['default'].WebVTT !== 'function') { 20291 if (track.tech_) { 20292 var loadHandler = function loadHandler() { 20293 return parseCues(responseBody, track); 20294 }; 20295 20296 track.tech_.on('vttjsloaded', loadHandler); 20297 track.tech_.on('vttjserror', function () { 20298 _log2['default'].error('vttjs failed to load, stopping trying to process ' + track.src); 20299 track.tech_.off('vttjsloaded', loadHandler); 20300 }); 20301 } 20302 } else { 20303 parseCues(responseBody, track); 20304 } 20305 })); 20306}; 20307 20308/** 20309 * A representation of a single `TextTrack`. 20310 * 20311 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrack} 20312 * @extends Track 20313 */ 20314 20315var TextTrack = function (_Track) { 20316 _inherits(TextTrack, _Track); 20317 20318 /** 20319 * Create an instance of this class. 20320 * 20321 * @param {Object} options={} 20322 * Object of option names and values 20323 * 20324 * @param {Tech} options.tech 20325 * A reference to the tech that owns this TextTrack. 20326 * 20327 * @param {TextTrack~Kind} [options.kind='subtitles'] 20328 * A valid text track kind. 20329 * 20330 * @param {TextTrack~Mode} [options.mode='disabled'] 20331 * A valid text track mode. 20332 * 20333 * @param {string} [options.id='vjs_track_' + Guid.newGUID()] 20334 * A unique id for this TextTrack. 20335 * 20336 * @param {string} [options.label=''] 20337 * The menu label for this track. 20338 * 20339 * @param {string} [options.language=''] 20340 * A valid two character language code. 20341 * 20342 * @param {string} [options.srclang=''] 20343 * A valid two character language code. An alternative, but deprioritized 20344 * vesion of `options.language` 20345 * 20346 * @param {string} [options.src] 20347 * A url to TextTrack cues. 20348 * 20349 * @param {boolean} [options.default] 20350 * If this track should default to on or off. 20351 */ 20352 function TextTrack() { 20353 var _this, _ret; 20354 20355 var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 20356 20357 _classCallCheck(this, TextTrack); 20358 20359 if (!options.tech) {
20360 throw new Error('A tech was not provided.'); 20361 } 20362 20363 var settings = (0, _mergeOptions2['default'])(options, { 20364 kind: _trackEnums.TextTrackKind[options.kind] || 'subtitles', 20365 language: options.language || options.srclang || '' 20366 }); 20367 var mode = _trackEnums.TextTrackMode[settings.mode] || 'disabled'; 20368 var default_ = settings['default']; 20369 20370 if (settings.kind === 'metadata' || settings.kind === 'chapters') { 20371 mode = 'hidden'; 20372 } 20373 // on IE8 this will be a document element 20374 // for every other browser this will be a normal object 20375 var tt = (_this = _possibleConstructorReturn(this, _Track.call(this, settings)), _this); 20376 20377 tt.tech_ = settings.tech; 20378 20379 if (browser.IS_IE8) { 20380 for (var prop in TextTrack.prototype) { 20381 if (prop !== 'constructor') { 20382 tt[prop] = TextTrack.prototype[prop]; 20383 } 20384 } 20385 } 20386 20387 tt.cues_ = []; 20388 tt.activeCues_ = []; 20389 20390 var cues = new _textTrackCueList2['default'](tt.cues_); 20391 var activeCues = new _textTrackCueList2['default'](tt.activeCues_); 20392 var changed = false; 20393 var timeupdateHandler = Fn.bind(tt, function () { 20394 20395 // Accessing this.activeCues for the side-effects of updating itself 20396 // due to it's nature as a getter function. Do not remove or cues will 20397 // stop updating! 20398 /* eslint-disable no-unused-expressions */ 20399 this.activeCues; 20400 /* eslint-enable no-unused-expressions */ 20401 if (changed) { 20402 this.trigger('cuechange'); 20403 changed = false; 20404 } 20405 }); 20406 20407 if (mode !== 'disabled') { 20408 tt.tech_.ready(function () { 20409 tt.tech_.on('timeupdate', timeupdateHandler); 20410 }, true); 20411 } 20412 20413 /** 20414 * @member {boolean} default 20415 * If this track was set to be on or off by default. Cannot be changed after 20416 * creation. 20417 * 20418 * @readonly 20419 */ 20420 Object.defineProperty(tt, 'default', { 20421 get: function get() { 20422 return default_; 20423 }, 20424 set: function set() {} 20425 }); 20426 20427 /** 20428 * @member {string} mode 20429 * Set the mode of this TextTrack to a valid {@link TextTrack~Mode}. Will 20430 * not be set if setting to an invalid mode. 20431 * 20432 * @fires TextTrack#modechange 20433 */ 20434 Object.defineProperty(tt, 'mode', { 20435 get: function get() { 20436 return mode; 20437 }, 20438 set: function set(newMode) { 20439 var _this2 = this; 20440 20441 if (!_trackEnums.TextTrackMode[newMode]) { 20442 return; 20443 } 20444 mode = newMode; 20445 if (mode === 'showing') { 20446 this.tech_.ready(function () { 20447 _this2.tech_.on('timeupdate', timeupdateHandler); 20448 }, true); 20449 } 20450 /** 20451 * An event that fires when mode changes on this track. This allows 20452 * the TextTrackList that holds this track to act accordingly. 20453 * 20454 * > Note: This is not part of the spec! 20455 * 20456 * @event TextTrack#modechange 20457 * @type {EventTarget~Event} 20458 */ 20459 this.trigger('modechange'); 20460 } 20461 }); 20462 20463 /** 20464 * @member {TextTrackCueList} cues 20465 * The text track cue list for this TextTrack. 20466 */ 20467 Object.defineProperty(tt, 'cues', { 20468 get: function get() { 20469 if (!this.loaded_) { 20470 return null; 20471 } 20472 20473 return cues; 20474 }, 20475 set: function set() {} 20476 }); 20477 20478 /** 20479 * @member {TextTrackCueList} activeCues 20480 * The list text track cues that are currently active for this TextTrack. 20481 */ 20482 Object.defineProperty(tt, 'activeCues', { 20483 get: function get() { 20484 if (!this.loaded_) { 20485 return null; 20486 } 20487 20488 // nothing to do 20489 if (this.cues.length === 0) { 20490 return activeCues; 20491 } 20492 20493 var ct = this.tech_.currentTime(); 20494 var active = []; 20495 20496 for (var i = 0, l = this.cues.length; i < l; i++) { 20497 var cue = this.cues[i]; 20498 20499 if (cue.startTime <= ct && cue.endTime >= ct) { 20500 active.push(cue); 20501 } else if (cue.startTime === cue.endTime && cue.startTime <= ct && cue.startTime + 0.5 >= ct) { 20502 active.push(cue); 20503 } 20504 } 20505 20506 changed = false;
20507 20508 if (active.length !== this.activeCues_.length) { 20509 changed = true; 20510 } else { 20511 for (var _i = 0; _i < active.length; _i++) { 20512 if (this.activeCues_.indexOf(active[_i]) === -1) { 20513 changed = true; 20514 } 20515 } 20516 } 20517 20518 this.activeCues_ = active; 20519 activeCues.setCues_(this.activeCues_); 20520 20521 return activeCues; 20522 }, 20523 set: function set() {} 20524 }); 20525 20526 if (settings.src) { 20527 tt.src = settings.src; 20528 loadTrack(settings.src, tt); 20529 } else { 20530 tt.loaded_ = true; 20531 } 20532 20533 return _ret = tt, _possibleConstructorReturn(_this, _ret); 20534 } 20535 20536 /** 20537 * Add a cue to the internal list of cues. 20538 * 20539 * @param {TextTrack~Cue} cue 20540 * The cue to add to our internal list 20541 */ 20542 20543 20544 TextTrack.prototype.addCue = function addCue(originalCue) { 20545 var cue = originalCue; 20546 20547 if (_window2['default'].vttjs && !(originalCue instanceof _window2['default'].vttjs.VTTCue)) { 20548 cue = new _window2['default'].vttjs.VTTCue(originalCue.startTime, originalCue.endTime, originalCue.text); 20549 20550 for (var prop in originalCue) { 20551 if (!(prop in cue)) { 20552 cue[prop] = originalCue[prop]; 20553 } 20554 } 20555 20556 // make sure that `id` is copied over 20557 cue.id = originalCue.id; 20558 cue.originalCue_ = originalCue; 20559 } 20560 20561 var tracks = this.tech_.textTracks(); 20562 20563 if (tracks) { 20564 for (var i = 0; i < tracks.length; i++) { 20565 if (tracks[i] !== this) { 20566 tracks[i].removeCue(cue); 20567 } 20568 } 20569 } 20570 20571 this.cues_.push(cue); 20572 this.cues.setCues_(this.cues_); 20573 }; 20574 20575 /** 20576 * Remove a cue from our internal list 20577 * 20578 * @param {TextTrack~Cue} removeCue 20579 * The cue to remove from our internal list 20580 */ 20581 20582 20583 TextTrack.prototype.removeCue = function removeCue(_removeCue) { 20584 var i = this.cues_.length; 20585 20586 while (i--) { 20587 var cue = this.cues_[i]; 20588 20589 if (cue === _removeCue || cue.originalCue_ && cue.originalCue_ === _removeCue) { 20590 this.cues_.splice(i, 1); 20591 this.cues.setCues_(this.cues_); 20592 break; 20593 } 20594 } 20595 }; 20596 20597 return TextTrack; 20598}(_track2['default']); 20599 20600/** 20601 * cuechange - One or more cues in the track have become active or stopped being active. 20602 */ 20603 20604 20605TextTrack.prototype.allowedEvents_ = { 20606 cuechange: 'cuechange' 20607}; 20608 20609exports['default'] = TextTrack; 20610 20611},{"104":104,"67":67,"73":73,"75":75,"78":78,"83":83,"86":86,"87":87,"92":92,"95":95}],73:[function(_dereq_,module,exports){ 20612'use strict'; 20613 20614exports.__esModule = true; 20615/** 20616 * @file track-kinds.js 20617 */ 20618 20619/** 20620 * All possible `VideoTrackKind`s 20621 * 20622 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-videotrack-kind 20623 * @typedef VideoTrack~Kind 20624 * @enum 20625 */ 20626var VideoTrackKind = exports.VideoTrackKind = { 20627 alternative: 'alternative', 20628 captions: 'captions', 20629 main: 'main', 20630 sign: 'sign', 20631 subtitles: 'subtitles', 20632 commentary: 'commentary' 20633}; 20634 20635/** 20636 * All possible `AudioTrackKind`s 20637 * 20638 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-audiotrack-kind 20639 * @typedef AudioTrack~Kind 20640 * @enum 20641 */ 20642var AudioTrackKind = exports.AudioTrackKind = { 20643 'alternative': 'alternative', 20644 'descriptions': 'descriptions', 20645 'main': 'main', 20646 'main-desc': 'main-desc', 20647 'translation': 'translation', 20648 'commentary': 'commentary' 20649}; 20650 20651/** 20652 * All possible `TextTrackKind`s 20653 * 20654 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-texttrack-kind 20655 * @typedef TextTrack~Kind 20656 * @enum 20657 */ 20658var TextTrackKind = exports.TextTrackKind = { 20659 subtitles: 'subtitles', 20660 captions: 'captions', 20661 descriptions: 'descriptions', 20662 chapters: 'chapters', 20663 metadata: 'metadata' 20664}; 20665 20666/** 20667 * All possible `TextTrackMode`s 20668 * 20669 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackmode 20670 * @typedef TextTrack~Mode 20671 * @enum 20672 */ 20673var TextTrackMode = exports.TextTrackMode = { 20674 disabled: 'disabled', 20675 hidden: 'hidden', 20676 showing: 'showing' 20677}; 20678 20679},{}],74:[function(_dereq_,module,exports){ 20680'use strict'; 20681 20682exports.__esModule = true; 20683 20684var _eventTarget = _dereq_(42); 20685 20686var _eventTarget2 = _interopRequireDefault(_eventTarget); 20687 20688var _browser = _dereq_(78); 20689 20690var browser = _interopRequireWildcard(_browser); 20691 20692var _document = _dereq_(94); 20693 20694var _document2 = _interopRequireDefault(_document); 20695 20696function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 20697 20698function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 20699 20700function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 20701 20702function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 20703 20704function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
20704onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 20705 * @file track-list.js 20706 */ 20707 20708 20709/** 20710 * Common functionaliy between {@link TextTrackList}, {@link AudioTrackList}, and 20711 * {@link VideoTrackList} 20712 * 20713 * @extends EventTarget 20714 */ 20715var TrackList = function (_EventTarget) { 20716 _inherits(TrackList, _EventTarget); 20717 20718 /** 20719 * Create an instance of this class 20720 * 20721 * @param {Track[]} tracks 20722 * A list of tracks to initialize the list with. 20723 * 20724 * @param {Object} [list] 20725 * The child object with inheritance done manually for ie8. 20726 * 20727 * @abstract 20728 */ 20729 function TrackList() { 20730 var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; 20731 20732 var _ret; 20733 20734 var list = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; 20735 20736 _classCallCheck(this, TrackList); 20737 20738 var _this = _possibleConstructorReturn(this, _EventTarget.call(this)); 20739 20740 if (!list) { 20741 list = _this; // eslint-disable-line 20742 if (browser.IS_IE8) { 20743 list = _document2['default'].createElement('custom'); 20744 for (var prop in TrackList.prototype) { 20745 if (prop !== 'constructor') { 20746 list[prop] = TrackList.prototype[prop]; 20747 } 20748 } 20749 } 20750 } 20751 20752 list.tracks_ = []; 20753 20754 /** 20755 * @member {number} length 20756 * The current number of `Track`s in the this Trackist. 20757 */ 20758 Object.defineProperty(list, 'length', { 20759 get: function get() { 20760 return this.tracks_.length; 20761 } 20762 }); 20763 20764 for (var i = 0; i < tracks.length; i++) { 20765 list.addTrack_(tracks[i]); 20766 } 20767 20768 // must return the object, as for ie8 it will not be this 20769 // but a reference to a document object 20770 return _ret = list, _possibleConstructorReturn(_this, _ret); 20771 } 20772 20773 /** 20774 * Add a {@link Track} to the `TrackList` 20775 * 20776 * @param {Track} track 20777 * The audio, video, or text track to add to the list. 20778 * 20779 * @fires TrackList#addtrack 20780 * @private 20781 */ 20782 20783 20784 TrackList.prototype.addTrack_ = function addTrack_(track) { 20785 var index = this.tracks_.length; 20786 20787 if (!('' + index in this)) { 20788 Object.defineProperty(this, index, { 20789 get: function get() { 20790 return this.tracks_[index]; 20791 } 20792 }); 20793 } 20794 20795 // Do not add duplicate tracks 20796 if (this.tracks_.indexOf(track) === -1) { 20797 this.tracks_.push(track); 20798 /** 20799 * Triggered when a track is added to a track list. 20800 * 20801 * @event TrackList#addtrack 20802 * @type {EventTarget~Event} 20803 * @property {Track} track 20804 * A reference to track that was added. 20805 */ 20806 this.trigger({ 20807 track: track, 20808 type: 'addtrack' 20809 }); 20810 } 20811 }; 20812 20813 /** 20814 * Remove a {@link Track} from the `TrackList` 20815 * 20816 * @param {Track} track 20817 * The audio, video, or text track to remove from the list. 20818 * 20819 * @fires TrackList#removetrack 20820 * @private 20821 */ 20822 20823 20824 TrackList.prototype.removeTrack_ = function removeTrack_(rtrack) { 20825 var track = void 0; 20826 20827 for (var i = 0, l = this.length; i < l; i++) { 20828 if (this[i] === rtrack) { 20829 track = this[i]; 20830 if (track.off) { 20831 track.off(); 20832 } 20833 20834 this.tracks_.splice(i, 1); 20835 20836 break; 20837 } 20838 } 20839 20840 if (!track) { 20841 return; 20842 } 20843 20844 /** 20845 * Triggered when a track is removed from track list. 20846 * 20847 * @event TrackList#removetrack 20848 * @type {EventTarget~Event} 20849 * @property {Track} track 20850 * A reference to track that was removed. 20851 */ 20852 this.trigger({ 20853 track: track, 20854 type: 'removetrack' 20855 }); 20856 }; 20857 20858 /** 20859 * Get a Track from the TrackList by a tracks id 20860 * 20861 * @param {String} id - the id of the track to get 20862 * @method getTrackById 20863 * @return {Track} 20864 * @private 20865 */ 20866 20867 20868 TrackList.prototype.getTrackById = function getTrackById(id) { 20869 var result = null; 20870 20871 for (var i = 0, l = this.length; i < l; i++) { 20872 var track = this[i]; 20873 20874 if (track.id === id) { 20875 result = track; 20876 break; 20877 } 20878 } 20879 20880 return result; 20881 }; 20882 20883 return TrackList; 20884}(_eventTarget2['default']); 20885 20886/** 20887 * Triggered when a different track is selected/enabled. 20888 * 20889 * @event TrackList#change 20890 * @type {EventTarget~Event} 20891 */ 20892 20893/** 20894 * Events that can be called with on + eventName. See {@link EventHandler}. 20895 * 20896 * @property {Object} TrackList#allowedEvents_ 20897 * @private 20898 */ 20899 20900 20901TrackList.prototype.allowedEvents_ = { 20902 change: 'change', 20903 addtrack: 'addtrack', 20904 removetrack: 'removetrack' 20905}; 20906 20907// emulate attribute EventHandler support to allow for feature detection 20908for (var event in TrackList.prototype.allowedEvents_) { 20909 TrackList.prototype['on' + event] = null; 20910} 20911 20912exports['default'] = TrackList; 20913 20914},{"42":42,"78":78,"94":94}],75:[function(_dereq_,module,exports){ 20915'use strict'; 20916 20917exports.__esModule = true; 20918 20919var _browser = _dereq_(78); 20920 20921var browser = _interopRequireWildcard(_browser); 20922 20923var _document = _dereq_(94); 20924 20925var _document2 = _interopRequireDefault(_document); 20926 20927var _guid = _dereq_(85); 20928 20929var Guid = _interopRequireWildcard(_guid); 20930 20931var _eventTarget = _dereq_(42); 20932 20933var _eventTarget2 = _interopRequireDefault(_eventTarget); 20934 20935function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 20936 20937function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 20938 20939function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 20940 20941function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 20942 20943function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
20943onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 20944 * @file track.js 20945 */ 20946 20947 20948/** 20949 * A Track class that contains all of the common functionality for {@link AudioTrack}, 20950 * {@link VideoTrack}, and {@link TextTrack}. 20951 * 20952 * > Note: This class should not be used directly 20953 * 20954 * @see {@link https://html.spec.whatwg.org/multipage/embedded-content.html} 20955 * @extends EventTarget 20956 * @abstract 20957 */ 20958var Track = function (_EventTarget) { 20959 _inherits(Track, _EventTarget); 20960 20961 /** 20962 * Create an instance of this class. 20963 * 20964 * @param {Object} [options={}] 20965 * Object of option names and values 20966 * 20967 * @param {string} [options.kind=''] 20968 * A valid kind for the track type you are creating. 20969 * 20970 * @param {string} [options.id='vjs_track_' + Guid.newGUID()] 20971 * A unique id for this AudioTrack. 20972 * 20973 * @param {string} [options.label=''] 20974 * The menu label for this track. 20975 * 20976 * @param {string} [options.language=''] 20977 * A valid two character language code. 20978 * 20979 * @abstract 20980 */ 20981 function Track() { 20982 var _ret; 20983 20984 var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 20985 20986 _classCallCheck(this, Track); 20987 20988 var _this = _possibleConstructorReturn(this, _EventTarget.call(this)); 20989 20990 var track = _this; // eslint-disable-line 20991 20992 if (browser.IS_IE8) { 20993 track = _document2['default'].createElement('custom'); 20994 for (var prop in Track.prototype) { 20995 if (prop !== 'constructor') { 20996 track[prop] = Track.prototype[prop]; 20997 } 20998 } 20999 } 21000 21001 var trackProps = { 21002 id: options.id || 'vjs_track_' + Guid.newGUID(), 21003 kind: options.kind || '', 21004 label: options.label || '', 21005 language: options.language || '' 21006 }; 21007 21008 /** 21009 * @member {string} id 21010 * The id of this track. Cannot be changed after creation. 21011 * 21012 * @readonly 21013 */ 21014 21015 /** 21016 * @member {string} kind 21017 * The kind of track that this is. Cannot be changed after creation. 21018 * 21019 * @readonly 21020 */ 21021 21022 /** 21023 * @member {string} label 21024 * The label of this track. Cannot be changed after creation. 21025 * 21026 * @readonly 21027 */ 21028 21029 /** 21030 * @member {string} language 21031 * The two letter language code for this track. Cannot be changed after 21032 * creation. 21033 * 21034 * @readonly 21035 */ 21036 21037 var _loop = function _loop(key) { 21038 Object.defineProperty(track, key, { 21039 get: function get() { 21040 return trackProps[key]; 21041 }, 21042 set: function set() {} 21043 }); 21044 }; 21045 21046 for (var key in trackProps) { 21047 _loop(key); 21048 } 21049 21050 return _ret = track, _possibleConstructorReturn(_this, _ret); 21051 } 21052 21053 return Track; 21054}(_eventTarget2['default']); 21055 21056exports['default'] = Track; 21057 21058},{"42":42,"78":78,"85":85,"94":94}],76:[function(_dereq_,module,exports){ 21059'use strict'; 21060 21061exports.__esModule = true; 21062 21063var _trackList = _dereq_(74); 21064 21065var _trackList2 = _interopRequireDefault(_trackList); 21066 21067var _browser = _dereq_(78); 21068 21069var browser = _interopRequireWildcard(_browser); 21070 21071var _document = _dereq_(94); 21072 21073var _document2 = _interopRequireDefault(_document); 21074 21075function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 21076 21077function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 21078 21079function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 21080 21081function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 21082 21083function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
21083onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 21084 * @file video-track-list.js 21085 */ 21086 21087 21088/** 21089 * Un-select all other {@link VideoTrack}s that are selected. 21090 * 21091 * @param {VideoTrackList} list 21092 * list to work on 21093 * 21094 * @param {VideoTrack} track 21095 * The track to skip 21096 * 21097 * @private 21098 */ 21099var disableOthers = function disableOthers(list, track) { 21100 for (var i = 0; i < list.length; i++) { 21101 if (track.id === list[i].id) { 21102 continue; 21103 } 21104 // another video track is enabled, disable it 21105 list[i].selected = false; 21106 } 21107}; 21108 21109/** 21110 * The current list of {@link VideoTrack} for a video. 21111 * 21112 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist} 21113 * @extends TrackList 21114 */ 21115 21116var VideoTrackList = function (_TrackList) { 21117 _inherits(VideoTrackList, _TrackList); 21118 21119 /** 21120 * Create an instance of this class. 21121 * 21122 * @param {VideoTrack[]} [tracks=[]] 21123 * A list of `VideoTrack` to instantiate the list with. 21124 */ 21125 function VideoTrackList() { 21126 var _this, _ret; 21127 21128 var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; 21129 21130 _classCallCheck(this, VideoTrackList); 21131 21132 var list = void 0; 21133 21134 // make sure only 1 track is enabled 21135 // sorted from last index to first index 21136 for (var i = tracks.length - 1; i >= 0; i--) { 21137 if (tracks[i].selected) { 21138 disableOthers(tracks, tracks[i]); 21139 break; 21140 } 21141 } 21142 21143 // IE8 forces us to implement inheritance ourselves 21144 // as it does not support Object.defineProperty properly 21145 if (browser.IS_IE8) { 21146 list = _document2['default'].createElement('custom'); 21147 for (var prop in _trackList2['default'].prototype) { 21148 if (prop !== 'constructor') { 21149 list[prop] = _trackList2['default'].prototype[prop]; 21150 } 21151 } 21152 for (var _prop in VideoTrackList.prototype) { 21153 if (_prop !== 'constructor') { 21154 list[_prop] = VideoTrackList.prototype[_prop]; 21155 } 21156 } 21157 } 21158
21159 list = (_this = _possibleConstructorReturn(this, _TrackList.call(this, tracks, list)), _this); 21160 list.changing_ = false; 21161 21162 /** 21163 * @member {number} VideoTrackList#selectedIndex 21164 * The current index of the selected {@link VideoTrack`}. 21165 */ 21166 Object.defineProperty(list, 'selectedIndex', { 21167 get: function get() { 21168 for (var _i = 0; _i < this.length; _i++) { 21169 if (this[_i].selected) { 21170 return _i; 21171 } 21172 } 21173 return -1; 21174 }, 21175 set: function set() {} 21176 }); 21177 21178 return _ret = list, _possibleConstructorReturn(_this, _ret); 21179 } 21180 21181 /** 21182 * Add a {@link VideoTrack} to the `VideoTrackList`. 21183 * 21184 * @param {VideoTrack} track 21185 * The VideoTrack to add to the list 21186 * 21187 * @fires TrackList#addtrack 21188 * @private 21189 */ 21190 21191 21192 VideoTrackList.prototype.addTrack_ = function addTrack_(track) { 21193 var _this2 = this; 21194 21195 if (track.selected) { 21196 disableOthers(this, track); 21197 } 21198 21199 _TrackList.prototype.addTrack_.call(this, track); 21200 // native tracks don't have this 21201 if (!track.addEventListener) { 21202 return; 21203 } 21204 21205 /** 21206 * @listens VideoTrack#selectedchange 21207 * @fires TrackList#change 21208 */ 21209 track.addEventListener('selectedchange', function () { 21210 if (_this2.changing_) { 21211 return; 21212 } 21213 _this2.changing_ = true; 21214 disableOthers(_this2, track); 21215 _this2.changing_ = false; 21216 _this2.trigger('change'); 21217 }); 21218 }; 21219 21220 /** 21221 * Add a {@link VideoTrack} to the `VideoTrackList`. 21222 * 21223 * @param {VideoTrack} track 21224 * The VideoTrack to add to the list 21225 * 21226 * @fires TrackList#addtrack 21227 */ 21228 21229 21230 VideoTrackList.prototype.addTrack = function addTrack(track) { 21231 this.addTrack_(track); 21232 }; 21233 21234 /** 21235 * Remove a {@link VideoTrack} to the `VideoTrackList`. 21236 * 21237 * @param {VideoTrack} track 21238 * The VideoTrack to remove from the list. 21239 * 21240 * @fires TrackList#removetrack 21241 */ 21242 21243 21244 VideoTrackList.prototype.removeTrack = function removeTrack(track) { 21245 _TrackList.prototype.removeTrack_.call(this, track); 21246 }; 21247 21248 return VideoTrackList; 21249}(_trackList2['default']); 21250 21251exports['default'] = VideoTrackList; 21252 21253},{"74":74,"78":78,"94":94}],77:[function(_dereq_,module,exports){ 21254'use strict'; 21255 21256exports.__esModule = true; 21257 21258var _trackEnums = _dereq_(73); 21259 21260var _track = _dereq_(75); 21261 21262var _track2 = _interopRequireDefault(_track); 21263 21264var _mergeOptions = _dereq_(87); 21265 21266var _mergeOptions2 = _interopRequireDefault(_mergeOptions); 21267 21268var _browser = _dereq_(78); 21269 21270var browser = _interopRequireWildcard(_browser); 21271 21272function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 21273 21274function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 21275 21276function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 21277 21278function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 21279 21280function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 21281 21282/** 21283 * A representation of a single `VideoTrack`. 21284 * 21285 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#videotrack} 21286 * @extends Track 21287 */ 21288var VideoTrack = function (_Track) { 21289 _inherits(VideoTrack, _Track); 21290 21291 /** 21292 * Create an instance of this class. 21293 * 21294 * @param {Object} [options={}] 21295 * Object of option names and values 21296 * 21297 * @param {string} [options.kind=''] 21298 * A valid {@link VideoTrack~Kind} 21299 * 21300 * @param {string} [options.id='vjs_track_' + Guid.newGUID()] 21301 * A unique id for this AudioTrack. 21302 * 21303 * @param {string} [options.label=''] 21304 * The menu label for this track. 21305 * 21306 * @param {string} [options.language=''] 21307 * A valid two character language code. 21308 * 21309 * @param {boolean} [options.selected] 21310 * If this track is the one that is currently playing. 21311 */ 21312 function VideoTrack() { 21313 var _this, _ret; 21314 21315 var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 21316 21317 _classCallCheck(this, VideoTrack); 21318 21319 var settings = (0, _mergeOptions2['default'])(options, { 21320 kind: _trackEnums.VideoTrackKind[options.kind] || '' 21321 }); 21322 21323 // on IE8 this will be a document element 21324 // for every other browser this will be a normal object
21325 var track = (_this = _possibleConstructorReturn(this, _Track.call(this, settings)), _this); 21326 var selected = false; 21327 21328 if (browser.IS_IE8) { 21329 for (var prop in VideoTrack.prototype) { 21330 if (prop !== 'constructor') { 21331 track[prop] = VideoTrack.prototype[prop]; 21332 } 21333 } 21334 } 21335 21336 /** 21337 * @member {boolean} selected 21338 * If this `VideoTrack` is selected or not. When setting this will 21339 * fire {@link VideoTrack#selectedchange} if the state of selected changed. 21340 * 21341 * @fires VideoTrack#selectedchange 21342 */ 21343 Object.defineProperty(track, 'selected', { 21344 get: function get() { 21345 return selected; 21346 }, 21347 set: function set(newSelected) { 21348 // an invalid or unchanged value 21349 if (typeof newSelected !== 'boolean' || newSelected === selected) { 21350 return; 21351 } 21352 selected = newSelected; 21353 21354 /** 21355 * An event that fires when selected changes on this track. This allows 21356 * the VideoTrackList that holds this track to act accordingly. 21357 * 21358 * > Note: This is not part of the spec! Native tracks will do 21359 * this internally without an event. 21360 * 21361 * @event VideoTrack#selectedchange 21362 * @type {EventTarget~Event} 21363 */ 21364 this.trigger('selectedchange'); 21365 } 21366 }); 21367 21368 // if the user sets this track to selected then 21369 // set selected to that true value otherwise 21370 // we keep it false 21371 if (settings.selected) { 21372 track.selected = settings.selected; 21373 } 21374 21375 return _ret = track, _possibleConstructorReturn(_this, _ret); 21376 } 21377 21378 return VideoTrack; 21379}(_track2['default']); 21380 21381exports['default'] = VideoTrack; 21382 21383},{"73":73,"75":75,"78":78,"87":87}],78:[function(_dereq_,module,exports){ 21384'use strict'; 21385 21386exports.__esModule = true; 21387exports.BACKGROUND_SIZE_SUPPORTED = exports.TOUCH_ENABLED = exports.IS_ANY_SAFARI = exports.IS_SAFARI = exports.IE_VERSION = exports.IS_IE8 = exports.CHROME_VERSION = exports.IS_CHROME = exports.IS_EDGE = exports.IS_FIREFOX = exports.IS_NATIVE_ANDROID = exp
21387orts.IS_OLD_ANDROID = exports.ANDROID_VERSION = exports.IS_ANDROID = exports.IOS_VERSION = exports.IS_IOS = exports.IS_IPOD = exports.IS_IPHONE = exports.IS_IPAD = undefined; 21388 21389var _dom = _dereq_(81); 21390 21391var Dom = _interopRequireWildcard(_dom); 21392 21393var _window = _dereq_(95); 21394 21395var _window2 = _interopRequireDefault(_window); 21396 21397function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 21398 21399function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 21400 21401/** 21402 * @file browser.js 21403 * @module browser 21404 */ 21405var USER_AGENT = _window2['default'].navigator && _window2['default'].navigator.userAgent || ''; 21406var webkitVersionMap = /AppleWebKit\/([\d.]+)/i.exec(USER_AGENT); 21407var appleWebkitVersion = webkitVersionMap ? parseFloat(webkitVersionMap.pop()) : null; 21408 21409/* 21410 * Device is an iPhone 21411 * 21412 * @type {Boolean} 21413 * @constant 21414 * @private 21415 */ 21416var IS_IPAD = exports.IS_IPAD = /iPad/i.test(USER_AGENT); 21417 21418// The Facebook app's UIWebView identifies as both an iPhone and iPad, so 21419// to identify iPhones, we need to exclude iPads. 21420// http://artsy.github.io/blog/2012/10/18/the-perils-of-ios-user-agent-sniffing/ 21421var IS_IPHONE = exports.IS_IPHONE = /iPhone/i.test(USER_AGENT) && !IS_IPAD; 21422var IS_IPOD = exports.IS_IPOD = /iPod/i.test(USER_AGENT); 21423var IS_IOS = exports.IS_IOS = IS_IPHONE || IS_IPAD || IS_IPOD; 21424 21425var IOS_VERSION = exports.IOS_VERSION = function () { 21426 var match = USER_AGENT.match(/OS (\d+)_/i); 21427 21428 if (match && match[1]) { 21429 return match[1]; 21430 } 21431 return null; 21432}(); 21433
21434var IS_ANDROID = exports.IS_ANDROID = /Android/i.test(USER_AGENT); 21435var ANDROID_VERSION = exports.ANDROID_VERSION = function () { 21436 // This matches Android Major.Minor.Patch versions 21437 // ANDROID_VERSION is Major.Minor as a Number, if Minor isn't available, then only Major is returned 21438 var match = USER_AGENT.match(/Android (\d+)(?:\.(\d+))?(?:\.(\d+))*/i); 21439 21440 if (!match) { 21441 return null; 21442 } 21443 21444 var major = match[1] && parseFloat(match[1]); 21445 var minor = match[2] && parseFloat(match[2]); 21446 21447 if (major && minor) { 21448 return parseFloat(match[1] + '.' + match[2]); 21449 } else if (major) { 21450 return major; 21451 } 21452 return null; 21453}(); 21454 21455// Old Android is defined as Version older than 2.3, and requiring a webkit version of the android browser 21456var IS_OLD_ANDROID = exports.IS_OLD_ANDROID = IS_ANDROID && /webkit/i.test(USER_AGENT) && ANDROID_VERSION < 2.3; 21457var IS_NATIVE_ANDROID = exports.IS_NATIVE_ANDROID = IS_ANDROID && ANDROID_VERSION < 5 && appleWebkitVersion < 537; 21458 21459var IS_FIREFOX = exports.IS_FIREFOX = /Firefox/i.test(USER_AGENT); 21460var IS_EDGE = exports.IS_EDGE = /Edge/i.test(USER_AGENT); 21461var IS_CHROME = exports.IS_CHROME = !IS_EDGE && /Chrome/i.test(USER_AGENT); 21462var CHROME_VERSION = exports.CHROME_VERSION = function () { 21463 var match = USER_AGENT.match(/Chrome\/(\d+)/); 21464 21465 if (match && match[1]) { 21466 return parseFloat(match[1]); 21467 } 21468 return null; 21469}(); 21470var IS_IE8 = exports.IS_IE8 = /MSIE\s8\.0/.test(USER_AGENT); 21471var IE_VERSION = exports.IE_VERSION = function () { 21472 var result = /MSIE\s(\d+)\.\d/.exec(USER_AGENT); 21473 var version = result && parseFloat(result[1]); 21474 21475 if (!version && /Trident\/7.0/i.test(USER_AGENT) && /rv:11.0/.test(USER_AGENT)) { 21476 // IE 11 has a different user agent string than other IE versions 21477 version = 11.0; 21478 } 21479 21480 return version; 21481}(); 21482 21483var IS_SAFARI = exports.IS_SAFARI = /Safari/i.test(USER_AGENT) && !IS_CHROME && !IS_ANDROID && !IS_EDGE; 21484var IS_ANY_SAFARI = exports.IS_ANY_SAFARI = IS_SAFARI || IS_IOS; 21485 21486var TOUCH_ENABLED = exports.TOUCH_ENABLED = Dom.isReal() && ('ontouchstart' in _window2['default'] || _window2['default'].DocumentTouch && _window2['default'].document instanceof _window2['default'].DocumentTouch); 21487 21488var BACKGROUND_SIZE_SUPPORTED = exports.BACKGROUND_SIZE_SUPPORTED = Dom.isReal() && 'backgroundSize' in _window2['default'].document.createElement('video').style; 21489 21490},{"81":81,"95":95}],79:[function(_dereq_,module,exports){ 21491'use strict'; 21492 21493exports.__esModule = true; 21494exports.bufferedPercent = bufferedPercent; 21495 21496var _timeRanges = _dereq_(90); 21497 21498/** 21499 * Compute the percentage of the media that has been buffered. 21500 * 21501 * @param {TimeRange} buffered 21502 * The current `TimeRange` object representing buffered time ranges 21503 * 21504 * @param {number} duration 21505 * Total duration of the media 21506 * 21507 * @return {number} 21508 * Percent buffered of the total duration in decimal form. 21509 */ 21510function bufferedPercent(buffered, duration) { 21511 var bufferedDuration = 0; 21512 var start = void 0; 21513 var end = void 0; 21514 21515 if (!duration) { 21516 return 0; 21517 } 21518 21519 if (!buffered || !buffered.length) { 21520 buffered = (0, _timeRanges.createTimeRange)(0, 0); 21521 } 21522 21523 for (var i = 0; i < buffered.length; i++) { 21524 start = buffered.start(i); 21525 end = buffered.end(i); 21526 21527 // buffered end can be bigger than duration by a very small fraction 21528 if (end > duration) { 21529 end = duration; 21530 } 21531 21532 bufferedDuration += end - start; 21533 } 21534 21535 return bufferedDuration / duration; 21536} /** 21537 * @file buffer.js 21538 * @module buffer 21539 */ 21540 21541},{"90":90}],80:[function(_dereq_,module,exports){ 21542'use strict'; 21543 21544exports.__esModule = true; 21545exports['default'] = computedStyle; 21546 21547var _window = _dereq_(95); 21548 21549var _window2 = _interopRequireDefault(_window); 21550 21551function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 21552 21553/** 21554 * A safe getComputedStyle with an IE8 fallback. 21555 * 21556 * This is needed because in Firefox, if the player is loaded in an iframe with 21557 * `display:none`, then `getComputedStyle` returns `null`, so, we do a null-check to 21558 * make sure that the player doesn't break in these cases. 21559 * 21560 * @param {Element} el 21561 * The element you want the computed style of 21562 * 21563 * @param {string} prop 21564 * The property name you want 21565 * 21566 * @see https://bugzilla.mozilla.org/show_bug.cgi?id=548397 21567 */ 21568function computedStyle(el, prop) { 21569 if (!el || !prop) { 21570 return ''; 21571 } 21572 21573 if (typeof _window2['default'].getComputedStyle === 'function') { 21574 var cs = _window2['default'].getComputedStyle(el); 21575 21576 return cs ? cs[prop] : ''; 21577 } 21578 21579 return el.currentStyle[prop] || ''; 21580} /** 21581 * @file computed-style.js 21582 * @module computed-style 21583 */ 21584 21585},{"95":95}],81:[function(_dereq_,module,exports){ 21586'use strict'; 21587 21588exports.__esModule = true; 21589exports.$$ = exports.$ = undefined; 21590
21591var _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 ', '.']); 21592 21593exports.isReal = isReal; 21594exports.isEl = isEl; 21595exports.getEl = getEl; 21596exports.createEl = createEl; 21597exports.textContent = textContent; 21598exports.insertElFirst = insertElFirst; 21599exports.getElData = getElData; 21600exports.hasElData = hasElData; 21601exports.removeElData = removeElData; 21602exports.hasElClass = hasElClass; 21603exports.addElClass = addElClass; 21604exports.removeElClass = removeElClass; 21605exports.toggleElClass = toggleElClass; 21606exports.setElAttributes = setElAttributes; 21607exports.getElAttributes = getElAttributes; 21608exports.getAttribute = getAttribute; 21609exports.setAttribute = setAttribute; 21610exports.removeAttribute = removeAttribute; 21611exports.blockTextSelection = blockTextSelection; 21612exports.unblockTextSelection = unblockTextSelection; 21613exports.findElPosition = findElPosition; 21614exports.getPointerPosition = getPointerPosition; 21615exports.isTextNode = isTextNode; 21616exports.emptyEl = emptyEl; 21617exports.normalizeContent = normalizeContent; 21618exports.appendContent = appendContent; 21619exports.insertContent = insertContent; 21620 21621var _document = _dereq_(94); 21622 21623var _document2 = _interopRequireDefault(_document); 21624 21625var _window = _dereq_(95); 21626 21627var _window2 = _interopRequireDefault(_window); 21628 21629var _guid = _dereq_(85); 21630 21631var Guid = _interopRequireWildcard(_guid); 21632 21633var _log = _dereq_(86); 21634 21635var _log2 = _interopRequireDefault(_log); 21636 21637var _tsml = _dereq_(98); 21638 21639var _tsml2 = _interopRequireDefault(_tsml); 21640 21641var _obj = _dereq_(88); 21642 21643function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 21644 21645function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 21646 21647function _taggedTemplateLiteralLoose(strings, raw) { strings.raw = raw; return strings; } /** 21648 * @file dom.js 21649 * @module dom 21650 */ 21651 21652 21653/** 21654 * Detect if a value is a string with any non-whitespace characters. 21655 * 21656 * @param {string} str 21657 * The string to check 21658 * 21659 * @return {boolean} 21660 * - True if the string is non-blank 21661 * - False otherwise 21662 * 21663 */ 21664function isNonBlankString(str) { 21665 return typeof str === 'string' && /\S/.test(str); 21666} 21667 21668/** 21669 * Throws an error if the passed string has whitespace. This is used by 21670 * class methods to be relatively consistent with the classList API. 21671 * 21672 * @param {string} str 21673 * The string to check for whitespace. 21674 * 21675 * @throws {Error} 21676 * Throws an error if there is whitespace in the string. 21677 * 21678 */ 21679function throwIfWhitespace(str) { 21680 if (/\s/.test(str)) { 21681 throw new Error('class has illegal whitespace characters'); 21682 } 21683} 21684 21685/** 21686 * Produce a regular expression for matching a className within an elements className. 21687 * 21688 * @param {string} className 21689 * The className to generate the RegExp for. 21690 * 21691 * @return {RegExp} 21692 * The RegExp that will check for a specific `className` in an elements 21693 * className. 21694 */ 21695function classRegExp(className) { 21696 return new RegExp('(^|\\s)' + className + '($|\\s)'); 21697} 21698 21699/** 21700 * Whether the current DOM interface appears to be real. 21701 * 21702 * @return {Boolean} 21703 */ 21704function isReal() { 21705 return ( 21706 21707 // Both document and window will never be undefined thanks to `global`. 21708 _document2['default'] === _window2['default'].document && 21709 21710 // In IE < 9, DOM methods return "object" as their type, so all we can 21711 // confidently check is that it exists. 21712 typeof _document2['default'].createElement !== 'undefined' 21713 ); 21714} 21715 21716/** 21717 * Determines, via duck typing, whether or not a value is a DOM element. 21718 * 21719 * @param {Mixed} value 21720 * The thing to check 21721 * 21722 * @return {boolean} 21723 * - True if it is a DOM element 21724 * - False otherwise 21725 */ 21726function isEl(value) { 21727 return (0, _obj.isObject)(value) && value.nodeType === 1; 21728} 21729 21730/** 21731 * Creates functions to query the DOM using a given method. 21732 * 21733 * @param {string} method 21734 * The method to create the query with. 21735 * 21736 * @return {Function} 21737 * The query method 21738 */ 21739function createQuerier(method) { 21740 return function (selector, context) { 21741 if (!isNonBlankString(selector)) { 21742 return _document2['default'][method](null); 21743 } 21744 if (isNonBlankString(context)) { 21745 context = _document2['default'].querySelector(context); 21746 } 21747 21748 var ctx = isEl(context) ? context : _document2['default']; 21749 21750 return ctx[method] && ctx[method](selector); 21751 }; 21752} 21753 21754/** 21755 * Shorthand for document.getElementById() 21756 * Also allows for CSS (jQuery) ID syntax. But nothing other than IDs. 21757 * 21758 * @param {string} id 21759 * The id of the element to get 21760 * 21761 * @return {Element|null} 21762 * Element with supplied ID or null if there wasn't one. 21763 */ 21764function getEl(id) { 21765 if (id.indexOf('#') === 0) { 21766 id = id.slice(1); 21767 } 21768 21769 return _document2['default'].getElementById(id); 21770} 21771 21772/** 21773 * Creates an element and applies properties. 21774 * 21775 * @param {string} [tagName='div'] 21776 * Name of tag to be created. 21777 * 21778 * @param {Object} [properties={}] 21779 * Element properties to be applied. 21780 * 21781 * @param {Object} [attributes={}] 21782 * Element attributes to be applied. 21783 * 21784 * @param {String|Element|TextNode|Array|Function} [content] 21785 * Contents for the element (see: {@link dom:normalizeContent}) 21786 * 21787 * @return {Element} 21788 * The element that was created. 21789 */ 21790function createEl() { 21791 var tagName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'div'; 21792 var properties = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 21793 var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; 21794 var content = arguments[3]; 21795 21796 var el = _document2['default'].createElement(tagName); 21797
vendor: 8,890 bytes, lines 21798-22145
21798 Object.getOwnPropertyNames(properties).forEach(function (propName) { 21799 var val = properties[propName]; 21800 21801 // See #2176 21802 // We originally were accepting both properties and attributes in the 21803 // same object, but that doesn't work so well. 21804 if (propName.indexOf('aria-') !== -1 || propName === 'role' || propName === 'type') { 21805 _log2['default'].warn((0, _tsml2['default'])(_templateObject, propName, val)); 21806 el.setAttribute(propName, val); 21807 21808 // Handle textContent since it's not supported everywhere and we have a 21809 // method for it. 21810 } else if (propName === 'textContent') { 21811 textContent(el, val); 21812 } else { 21813 el[propName] = val; 21814 } 21815 }); 21816 21817 Object.getOwnPropertyNames(attributes).forEach(function (attrName) { 21818 el.setAttribute(attrName, attributes[attrName]); 21819 }); 21820 21821 if (content) { 21822 appendContent(el, content); 21823 } 21824 21825 return el; 21826} 21827 21828/** 21829 * Injects text into an element, replacing any existing contents entirely. 21830 * 21831 * @param {Element} el 21832 * The element to add text content into 21833 * 21834 * @param {string} text 21835 * The text content to add. 21836 * 21837 * @return {Element} 21838 * The element with added text content. 21839 */ 21840function textContent(el, text) { 21841 if (typeof el.textContent === 'undefined') { 21842 el.innerText = text; 21843 } else { 21844 el.textContent = text; 21845 } 21846 return el; 21847} 21848 21849/** 21850 * Insert an element as the first child node of another 21851 * 21852 * @param {Element} child 21853 * Element to insert 21854 * 21855 * @param {Element} parent 21856 * Element to insert child into 21857 * 21858 */ 21859function insertElFirst(child, parent) { 21860 if (parent.firstChild) { 21861 parent.insertBefore(child, parent.firstChild); 21862 } else { 21863 parent.appendChild(child); 21864 } 21865} 21866 21867/** 21868 * Element Data Store. Allows for binding data to an element without putting it directly on the element. 21869 * Ex. Event listeners are stored here. 21870 * (also from jsninja.com, slightly modified and updated for closure compiler) 21871 * 21872 * @type {Object} 21873 * @private 21874 */ 21875var elData = {}; 21876 21877/* 21878 * Unique attribute name to store an element's guid in 21879 * 21880 * @type {string} 21881 * @constant 21882 * @private 21883 */ 21884var elIdAttr = 'vdata' + new Date().getTime(); 21885 21886/** 21887 * Returns the cache object where data for an element is stored 21888 * 21889 * @param {Element} el 21890 * Element to store data for. 21891 * 21892 * @return {Object} 21893 * The cache object for that el that was passed in. 21894 */ 21895function getElData(el) { 21896 var id = el[elIdAttr]; 21897 21898 if (!id) { 21899 id = el[elIdAttr] = Guid.newGUID(); 21900 } 21901 21902 if (!elData[id]) { 21903 elData[id] = {}; 21904 } 21905 21906 return elData[id]; 21907} 21908 21909/** 21910 * Returns whether or not an element has cached data 21911 * 21912 * @param {Element} el 21913 * Check if this element has cached data. 21914 * 21915 * @return {boolean} 21916 * - True if the DOM element has cached data. 21917 * - False otherwise. 21918 */ 21919function hasElData(el) { 21920 var id = el[elIdAttr]; 21921 21922 if (!id) { 21923 return false; 21924 } 21925 21926 return !!Object.getOwnPropertyNames(elData[id]).length; 21927} 21928 21929/** 21930 * Delete data for the element from the cache and the guid attr from getElementById 21931 * 21932 * @param {Element} el 21933 * Remove cached data for this element. 21934 */ 21935function removeElData(el) { 21936 var id = el[elIdAttr]; 21937 21938 if (!id) { 21939 return; 21940 } 21941 21942 // Remove all stored data 21943 delete elData[id]; 21944 21945 // Remove the elIdAttr property from the DOM node 21946 try { 21947 delete el[elIdAttr]; 21948 } catch (e) { 21949 if (el.removeAttribute) { 21950 el.removeAttribute(elIdAttr); 21951 } else { 21952 // IE doesn't appear to support removeAttribute on the document element 21953 el[elIdAttr] = null; 21954 } 21955 } 21956} 21957 21958/** 21959 * Check if an element has a CSS class 21960 * 21961 * @param {Element} element 21962 * Element to check 21963 * 21964 * @param {string} classToCheck 21965 * Class name to check for 21966 * 21967 * @return {boolean} 21968 * - True if the element had the class 21969 * - False otherwise. 21970 * 21971 * @throws {Error} 21972 * Throws an error if `classToCheck` has white space. 21973 */ 21974function hasElClass(element, classToCheck) { 21975 throwIfWhitespace(classToCheck); 21976 if (element.classList) { 21977 return element.classList.contains(classToCheck); 21978 } 21979 return classRegExp(classToCheck).test(element.className); 21980} 21981 21982/** 21983 * Add a CSS class name to an element 21984 * 21985 * @param {Element} element 21986 * Element to add class name to. 21987 * 21988 * @param {string} classToAdd 21989 * Class name to add. 21990 * 21991 * @return {Element} 21992 * The dom element with the added class name. 21993 */ 21994function addElClass(element, classToAdd) { 21995 if (element.classList) { 21996 element.classList.add(classToAdd); 21997 21998 // Don't need to `throwIfWhitespace` here because `hasElClass` will do it 21999 // in the case of classList not being supported. 22000 } else if (!hasElClass(element, classToAdd)) { 22001 element.className = (element.className + ' ' + classToAdd).trim(); 22002 } 22003 22004 return element; 22005} 22006 22007/** 22008 * Remove a CSS class name from an element 22009 * 22010 * @param {Element} element 22011 * Element to remove a class name from. 22012 * 22013 * @param {string} classToRemove 22014 * Class name to remove 22015 * 22016 * @return {Element} 22017 * The dom element with class name removed. 22018 */ 22019function removeElClass(element, classToRemove) { 22020 if (element.classList) { 22021 element.classList.remove(classToRemove); 22022 } else { 22023 throwIfWhitespace(classToRemove); 22024 element.className = element.className.split(/\s+/).filter(function (c) { 22025 return c !== classToRemove; 22026 }).join(' '); 22027 } 22028 22029 return element; 22030} 22031 22032/** 22033 * The callback definition for toggleElClass. 22034 * 22035 * @callback Dom~PredicateCallback 22036 * @param {Element} element 22037 * The DOM element of the Component. 22038 * 22039 * @param {string} classToToggle 22040 * The `className` that wants to be toggled 22041 * 22042 * @return {boolean|undefined} 22043 * - If true the `classToToggle` will get added to `element`. 22044 * - If false the `classToToggle` will get removed from `element`. 22045 * - If undefined this callback will be ignored 22046 */ 22047 22048/** 22049 * Adds or removes a CSS class name on an element depending on an optional 22050 * condition or the presence/absence of the class name. 22051 * 22052 * @param {Element} element 22053 * The element to toggle a class name on. 22054 * 22055 * @param {string} classToToggle 22056 * The class that should be toggled 22057 * 22058 * @param {boolean|PredicateCallback} [predicate] 22059 * See the return value for {@link Dom~PredicateCallback} 22060 * 22061 * @return {Element} 22062 * The element with a class that has been toggled. 22063 */ 22064function toggleElClass(element, classToToggle, predicate) { 22065 22066 // This CANNOT use `classList` internally because IE does not support the 22067 // second parameter to the `classList.toggle()` method! Which is fine because 22068 // `classList` will be used by the add/remove functions. 22069 var has = hasElClass(element, classToToggle); 22070 22071 if (typeof predicate === 'function') { 22072 predicate = predicate(element, classToToggle); 22073 } 22074 22075 if (typeof predicate !== 'boolean') { 22076 predicate = !has; 22077 } 22078 22079 // If the necessary class operation matches the current state of the 22080 // element, no action is required. 22081 if (predicate === has) { 22082 return; 22083 } 22084 22085 if (predicate) { 22086 addElClass(element, classToToggle); 22087 } else { 22088 removeElClass(element, classToToggle); 22089 } 22090 22091 return element; 22092} 22093 22094/** 22095 * Apply attributes to an HTML element. 22096 * 22097 * @param {Element} el 22098 * Element to add attributes to. 22099 * 22100 * @param {Object} [attributes] 22101 * Attributes to be applied. 22102 */ 22103function setElAttributes(el, attributes) { 22104 Object.getOwnPropertyNames(attributes).forEach(function (attrName) { 22105 var attrValue = attributes[attrName]; 22106 22107 if (attrValue === null || typeof attrValue === 'undefined' || attrValue === false) { 22108 el.removeAttribute(attrName); 22109 } else { 22110 el.setAttribute(attrName, attrValue === true ? '' : attrValue); 22111 } 22112 }); 22113} 22114 22115/** 22116 * Get an element's attribute values, as defined on the HTML tag 22117 * Attributes are not the same as properties. They're defined on the tag 22118 * or with setAttribute (which shouldn't be used with HTML) 22119 * This will return true or false for boolean attributes. 22120 * 22121 * @param {Element} tag 22122 * Element from which to get tag attributes. 22123 * 22124 * @return {Object} 22125 * All attributes of the element. 22126 */ 22127function getElAttributes(tag) { 22128 var obj = {}; 22129 22130 // known boolean attributes 22131 // we can check for matching boolean properties, but older browsers 22132 // won't know about HTML5 boolean attributes that we still read from 22133 var knownBooleans = ',' + 'autoplay,controls,loop,muted,default' + ','; 22134 22135 if (tag && tag.attributes && tag.attributes.length > 0) { 22136 var attrs = tag.attributes; 22137 22138 for (var i = attrs.length - 1; i >= 0; i--) { 22139 var attrName = attrs[i].name; 22140 var attrVal = attrs[i].value; 22141 22142 // check for known booleans 22143 // the matching element property will return a value for typeof 22144 if (typeof tag[attrName] === 'boolean' || knownBooleans.indexOf(',' + attrName + ',') !== -1) { 22145 // the value of an included boolean attribute is typically an empty
22146 // string ('') which would equal false if we just check for a false value. 22147 // we also don't want support bad code like autoplay='false' 22148 attrVal = attrVal !== null ? true : false; 22149 } 22150 22151 obj[attrName] = attrVal; 22152 } 22153 } 22154 22155 return obj; 22156} 22157 22158/** 22159 * Get the value of an element's attribute 22160 * 22161 * @param {Element} el 22162 * A DOM element 22163 * 22164 * @param {string} attribute 22165 * Attribute to get the value of 22166 * 22167 * @return {string} 22168 * value of the attribute 22169 */ 22170function getAttribute(el, attribute) { 22171 return el.getAttribute(attribute); 22172} 22173 22174/** 22175 * Set the value of an element's attribute 22176 * 22177 * @param {Element} el 22178 * A DOM element 22179 * 22180 * @param {string} attribute 22181 * Attribute to set 22182 * 22183 * @param {string} value 22184 * Value to set the attribute to 22185 */ 22186function setAttribute(el, attribute, value) { 22187 el.setAttribute(attribute, value); 22188} 22189 22190/** 22191 * Remove an element's attribute 22192 * 22193 * @param {Element} el 22194 * A DOM element 22195 * 22196 * @param {string} attribute 22197 * Attribute to remove 22198 */ 22199function removeAttribute(el, attribute) { 22200 el.removeAttribute(attribute); 22201} 22202 22203/** 22204 * Attempt to block the ability to select text while dragging controls 22205 */ 22206function blockTextSelection() { 22207 _document2['default'].body.focus(); 22208 _document2['default'].onselectstart = function () { 22209 return false; 22210 }; 22211} 22212 22213/** 22214 * Turn off text selection blocking 22215 */ 22216function unblockTextSelection() { 22217 _document2['default'].onselectstart = function () { 22218 return true; 22219 }; 22220} 22221 22222/** 22223 * The postion of a DOM element on the page. 22224 * 22225 * @typedef {Object} Dom~Position 22226 * 22227 * @property {number} left 22228 * Pixels to the left 22229 * 22230 * @property {number} top 22231 * Pixels on top 22232 */ 22233 22234/** 22235 * Offset Left. 22236 * getBoundingClientRect technique from 22237 * John Resig 22238 * 22239 * @see http://ejohn.org/blog/getboundingclientrect-is-awesome/ 22240 * 22241 * @param {Element} el 22242 * Element from which to get offset 22243 * 22244 * @return {Dom~Position} 22245 * The position of the element that was passed in. 22246 */ 22247function findElPosition(el) { 22248 var box = void 0; 22249 22250 if (el.getBoundingClientRect && el.parentNode) { 22251 box = el.getBoundingClientRect(); 22252 } 22253 22254 if (!box) { 22255 return { 22256 left: 0, 22257 top: 0 22258 }; 22259 } 22260 22261 var docEl = _document2['default'].documentElement; 22262 var body = _document2['default'].body; 22263 22264 var clientLeft = docEl.clientLeft || body.clientLeft || 0; 22265 var scrollLeft = _window2['default'].pageXOffset || body.scrollLeft; 22266 var left = box.left + scrollLeft - clientLeft; 22267 22268 var clientTop = docEl.clientTop || body.clientTop || 0; 22269 var scrollTop = _window2['default'].pageYOffset || body.scrollTop; 22270 var top = box.top + scrollTop - clientTop; 22271 22272 // Android sometimes returns slightly off decimal values, so need to round 22273 return { 22274 left: Math.round(left), 22275 top: Math.round(top) 22276 }; 22277} 22278 22279/** 22280 * x and y coordinates for a dom element or mouse pointer 22281 * 22282 * @typedef {Object} Dom~Coordinates 22283 * 22284 * @property {number} x 22285 * x coordinate in pixels 22286 * 22287 * @property {number} y 22288 * y coordinate in pixels 22289 */ 22290 22291/** 22292 * Get pointer position in element 22293 * Returns an object with x and y coordinates. 22294 * The base on the coordinates are the bottom left of the element. 22295 * 22296 * @param {Element} el 22297 * Element on which to get the pointer position on 22298 * 22299 * @param {EventTarget~Event} event 22300 * Event object 22301 * 22302 * @return {Dom~Coordinates} 22303 * A Coordinates object corresponding to the mouse position. 22304 * 22305 */ 22306function getPointerPosition(el, event) { 22307 var position = {}; 22308 var box = findElPosition(el); 22309 var boxW = el.offsetWidth; 22310 var boxH = el.offsetHeight; 22311 22312 var boxY = box.top; 22313 var boxX = box.left; 22314 var pageY = event.pageY; 22315 var pageX = event.pageX; 22316 22317 if (event.changedTouches) { 22318 pageX = event.changedTouches[0].pageX; 22319 pageY = event.changedTouches[0].pageY; 22320 } 22321 22322 position.y = Math.max(0, Math.min(1, (boxY - pageY + boxH) / boxH)); 22323 position.x = Math.max(0, Math.min(1, (pageX - boxX) / boxW)); 22324 22325 return position; 22326} 22327 22328/** 22329 * Determines, via duck typing, whether or not a value is a text node. 22330 * 22331 * @param {Mixed} value 22332 * Check if this value is a text node. 22333 * 22334 * @return {boolean} 22335 * - True if it is a text node 22336 * - False otherwise 22337 */ 22338function isTextNode(value) { 22339 return (0, _obj.isObject)(value) && value.nodeType === 3; 22340} 22341
vendor: 4,080 bytes, lines 22342-22473
22342/** 22343 * Empties the contents of an element. 22344 * 22345 * @param {Element} el 22346 * The element to empty children from 22347 * 22348 * @return {Element} 22349 * The element with no children 22350 */ 22351function emptyEl(el) { 22352 while (el.firstChild) { 22353 el.removeChild(el.firstChild); 22354 } 22355 return el; 22356} 22357 22358/** 22359 * Normalizes content for eventual insertion into the DOM. 22360 * 22361 * This allows a wide range of content definition methods, but protects 22362 * from falling into the trap of simply writing to `innerHTML`, which is 22363 * an XSS concern. 22364 * 22365 * The content for an element can be passed in multiple types and 22366 * combinations, whose behavior is as follows: 22367 * 22368 * @param {String|Element|TextNode|Array|Function} content 22369 * - String: Normalized into a text node. 22370 * - Element/TextNode: Passed through. 22371 * - Array: A one-dimensional array of strings, elements, nodes, or functions 22372 * (which return single strings, elements, or nodes). 22373 * - Function: If the sole argument, is expected to produce a string, element, 22374 * node, or array as defined above. 22375 * 22376 * @return {Array} 22377 * All of the content that was passed in normalized. 22378 */ 22379function normalizeContent(content) { 22380 22381 // First, invoke content if it is a function. If it produces an array, 22382 // that needs to happen before normalization. 22383 if (typeof content === 'function') { 22384 content = content(); 22385 } 22386 22387 // Next up, normalize to an array, so one or many items can be normalized, 22388 // filtered, and returned. 22389 return (Array.isArray(content) ? content : [content]).map(function (value) { 22390 22391 // First, invoke value if it is a function to produce a new value, 22392 // which will be subsequently normalized to a Node of some kind. 22393 if (typeof value === 'function') { 22394 value = value(); 22395 } 22396 22397 if (isEl(value) || isTextNode(value)) { 22398 return value; 22399 } 22400 22401 if (typeof value === 'string' && /\S/.test(value)) { 22402 return _document2['default'].createTextNode(value); 22403 } 22404 }).filter(function (value) { 22405 return value; 22406 }); 22407} 22408 22409/** 22410 * Normalizes and appends content to an element. 22411 * 22412 * @param {Element} el 22413 * Element to append normalized content to. 22414 * 22415 * 22416 * @param {String|Element|TextNode|Array|Function} content 22417 * See the `content` argument of {@link dom:normalizeContent} 22418 * 22419 * @return {Element} 22420 * The element with appended normalized content. 22421 */ 22422function appendContent(el, content) { 22423 normalizeContent(content).forEach(function (node) { 22424 return el.appendChild(node); 22425 }); 22426 return el; 22427} 22428 22429/** 22430 * Normalizes and inserts content into an element; this is identical to 22431 * `appendContent()`, except it empties the element first. 22432 * 22433 * @param {Element} el 22434 * Element to insert normalized content into. 22435 * 22436 * @param {String|Element|TextNode|Array|Function} content 22437 * See the `content` argument of {@link dom:normalizeContent} 22438 * 22439 * @return {Element} 22440 * The element with inserted normalized content. 22441 * 22442 */ 22443function insertContent(el, content) { 22444 return appendContent(emptyEl(el), content); 22445} 22446 22447/** 22448 * Finds a single DOM element matching `selector` within the optional 22449 * `context` of another DOM element (defaulting to `document`). 22450 * 22451 * @param {string} selector 22452 * A valid CSS selector, which will be passed to `querySelector`. 22453 * 22454 * @param {Element|String} [context=document] 22455 * A DOM element within which to query. Can also be a selector 22456 * string in which case the first matching element will be used 22457 * as context. If missing (or no element matches selector), falls 22458 * back to `document`. 22459 * 22460 * @return {Element|null} 22461 * The element that was found or null. 22462 */ 22463var $ = exports.$ = createQuerier('querySelector'); 22464 22465/** 22466 * Finds a all DOM elements matching `selector` within the optional 22467 * `context` of another DOM element (defaulting to `document`). 22468 * 22469 * @param {string} selector 22470 * A valid CSS selector, which will be passed to `querySelectorAll`. 22471 * 22472 * @param {Element|String} [context=document] 22473 * A DOM element within which to query. Can also be a selector
22474 * string in which case the first matching element will be used 22475 * as context. If missing (or no element matches selector), falls 22476 * back to `document`. 22477 * 22478 * @return {NodeList} 22479 * A element list of elements that were found. Will be empty if none were found. 22480 * 22481 */ 22482var $$ = exports.$$ = createQuerier('querySelectorAll'); 22483 22484},{"85":85,"86":86,"88":88,"94":94,"95":95,"98":98}],82:[function(_dereq_,module,exports){ 22485'use strict'; 22486 22487exports.__esModule = true; 22488exports.fixEvent = fixEvent; 22489exports.on = on; 22490exports.off = off; 22491exports.trigger = trigger; 22492exports.one = one; 22493 22494var _dom = _dereq_(81); 22495 22496var Dom = _interopRequireWildcard(_dom); 22497 22498var _guid = _dereq_(85); 22499 22500var Guid = _interopRequireWildcard(_guid); 22501 22502var _log = _dereq_(86); 22503 22504var _log2 = _interopRequireDefault(_log); 22505 22506var _window = _dereq_(95); 22507 22508var _window2 = _interopRequireDefault(_window); 22509 22510var _document = _dereq_(94); 22511 22512var _document2 = _interopRequireDefault(_document); 22513 22514function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 22515 22516function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 22517 22518/** 22519 * Clean up the listener cache and dispatchers 22520 * 22521 * @param {Element|Object} elem 22522 * Element to clean up 22523 * 22524 * @param {string} type 22525 * Type of event to clean up 22526 */ 22527function _cleanUpEvents(elem, type) { 22528 var data = Dom.getElData(elem); 22529 22530 // Remove the events of a particular type if there are none left 22531 if (data.handlers[type].length === 0) { 22532 delete data.handlers[type]; 22533 // data.handlers[type] = null; 22534 // Setting to null was causing an error with data.handlers 22535 22536 // Remove the meta-handler from the element 22537 if (elem.removeEventListener) { 22538 elem.removeEventListener(type, data.dispatcher, false); 22539 } else if (elem.detachEvent) { 22540 elem.detachEvent('on' + type, data.dispatcher); 22541 } 22542 } 22543 22544 // Remove the events object if there are no types left 22545 if (Object.getOwnPropertyNames(data.handlers).length <= 0) { 22546 delete data.handlers; 22547 delete data.dispatcher; 22548 delete data.disabled; 22549 } 22550 22551 // Finally remove the element data if there is no data left 22552 if (Object.getOwnPropertyNames(data).length === 0) { 22553 Dom.removeElData(elem); 22554 } 22555} 22556 22557/** 22558 * Loops through an array of event types and calls the requested method for each type. 22559 * 22560 * @param {Function} fn 22561 * The event method we want to use. 22562 * 22563 * @param {Element|Object} elem 22564 * Element or object to bind listeners to 22565 * 22566 * @param {string} type 22567 * Type of event to bind to. 22568 * 22569 * @param {EventTarget~EventListener} callback 22570 * Event listener. 22571 */ 22572/** 22573 * @file events.js. An Event System (John Resig - Secrets of a JS Ninja http://jsninja.com/) 22574 * (Original book version wasn't completely usable, so fixed some things and made Closure Compiler compatible) 22575 * This should work very similarly to jQuery's events, however it's based off the book version which isn't as 22576 * robust as jquery's, so there's probably some differences. 22577 * 22578 * @module events 22579 */ 22580 22581function _handleMultipleEvents(fn, elem, types, callback) { 22582 types.forEach(function (type) { 22583 // Call the event method for each one of the types 22584 fn(elem, type, callback); 22585 }); 22586} 22587 22588/** 22589 * Fix a native event to have standard property values 22590 * 22591 * @param {Object} event 22592 * Event object to fix. 22593 * 22594 * @return {Object} 22595 * Fixed event object. 22596 */ 22597function fixEvent(event) { 22598 22599 function returnTrue() { 22600 return true; 22601 } 22602 22603 function returnFalse() { 22604 return false; 22605 } 22606 22607 // Test if fixing up is needed 22608 // Used to check if !event.stopPropagation instead of isPropagationStopped 22609 // But native events return true for stopPropagation, but don't have 22610 // other expected methods like isPropagationStopped. Seems to be a problem 22611 // with the Javascript Ninja code. So we're just overriding all events now. 22612 if (!event || !event.isPropagationStopped) { 22613 var old = event || _window2['default'].event; 22614 22615 event = {}; 22616 // Clone the old object so that we can modify the values event = {}; 22617 // IE8 Doesn't like when you mess with native event properties 22618 // Firefox returns false for event.hasOwnProperty('type') and other props 22619 // which makes copying more difficult. 22620 // TODO: Probably best to create a whitelist of event props 22621 for (var key in old) {
vendor: 8,973 bytes, lines 22622-22938
22622 // Safari 6.0.3 warns you if you try to copy deprecated layerX/Y 22623 // Chrome warns you if you try to copy deprecated keyboardEvent.keyLocation 22624 // and webkitMovementX/Y 22625 if (key !== 'layerX' && key !== 'layerY' && key !== 'keyLocation' && key !== 'webkitMovementX' && key !== 'webkitMovementY') { 22626 // Chrome 32+ warns if you try to copy deprecated returnValue, but 22627 // we still want to if preventDefault isn't supported (IE8). 22628 if (!(key === 'returnValue' && old.preventDefault)) { 22629 event[key] = old[key]; 22630 } 22631 } 22632 } 22633 22634 // The event occurred on this element 22635 if (!event.target) { 22636 event.target = event.srcElement || _document2['default']; 22637 } 22638 22639 // Handle which other element the event is related to 22640 if (!event.relatedTarget) { 22641 event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement; 22642 } 22643 22644 // Stop the default browser action 22645 event.preventDefault = function () { 22646 if (old.preventDefault) { 22647 old.preventDefault(); 22648 } 22649 event.returnValue = false; 22650 old.returnValue = false; 22651 event.defaultPrevented = true; 22652 }; 22653 22654 event.defaultPrevented = false; 22655 22656 // Stop the event from bubbling 22657 event.stopPropagation = function () { 22658 if (old.stopPropagation) { 22659 old.stopPropagation(); 22660 } 22661 event.cancelBubble = true; 22662 old.cancelBubble = true; 22663 event.isPropagationStopped = returnTrue; 22664 }; 22665 22666 event.isPropagationStopped = returnFalse; 22667 22668 // Stop the event from bubbling and executing other handlers 22669 event.stopImmediatePropagation = function () { 22670 if (old.stopImmediatePropagation) { 22671 old.stopImmediatePropagation(); 22672 } 22673 event.isImmediatePropagationStopped = returnTrue; 22674 event.stopPropagation(); 22675 }; 22676 22677 event.isImmediatePropagationStopped = returnFalse; 22678 22679 // Handle mouse position 22680 if (event.clientX !== null && event.clientX !== undefined) { 22681 var doc = _document2['default'].documentElement; 22682 var body = _document2['default'].body; 22683 22684 event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); 22685 event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0); 22686 } 22687 22688 // Handle key presses 22689 event.which = event.charCode || event.keyCode; 22690 22691 // Fix button for mouse clicks: 22692 // 0 == left; 1 == middle; 2 == right 22693 if (event.button !== null && event.button !== undefined) { 22694 22695 // The following is disabled because it does not pass videojs-standard 22696 // and... yikes. 22697 /* eslint-disable */ 22698 event.button = event.button & 1 ? 0 : event.button & 4 ? 1 : event.button & 2 ? 2 : 0; 22699 /* eslint-enable */ 22700 } 22701 } 22702 22703 // Returns fixed-up instance 22704 return event; 22705} 22706 22707/** 22708 * Whether passive event listeners are supported 22709 */ 22710var _supportsPassive = false; 22711 22712(function () { 22713 try { 22714 var opts = Object.defineProperty({}, 'passive', { 22715 get: function get() { 22716 _supportsPassive = true; 22717 } 22718 }); 22719 22720 _window2['default'].addEventListener('test', null, opts); 22721 } catch (e) { 22722 // disregard 22723 } 22724})(); 22725 22726/** 22727 * Touch events Chrome expects to be passive 22728 */ 22729var passiveEvents = ['touchstart', 'touchmove']; 22730 22731/** 22732 * Add an event listener to element 22733 * It stores the handler function in a separate cache object 22734 * and adds a generic handler to the element's event, 22735 * along with a unique id (guid) to the element. 22736 * 22737 * @param {Element|Object} elem 22738 * Element or object to bind listeners to 22739 * 22740 * @param {string|string[]} type 22741 * Type of event to bind to. 22742 * 22743 * @param {EventTarget~EventListener} fn 22744 * Event listener. 22745 */ 22746function on(elem, type, fn) { 22747 if (Array.isArray(type)) { 22748 return _handleMultipleEvents(on, elem, type, fn); 22749 } 22750 22751 var data = Dom.getElData(elem); 22752 22753 // We need a place to store all our handler data 22754 if (!data.handlers) { 22755 data.handlers = {}; 22756 } 22757 22758 if (!data.handlers[type]) { 22759 data.handlers[type] = []; 22760 } 22761 22762 if (!fn.guid) { 22763 fn.guid = Guid.newGUID(); 22764 } 22765 22766 data.handlers[type].push(fn); 22767 22768 if (!data.dispatcher) { 22769 data.disabled = false; 22770 22771 data.dispatcher = function (event, hash) { 22772 22773 if (data.disabled) { 22774 return; 22775 } 22776 22777 event = fixEvent(event); 22778 22779 var handlers = data.handlers[event.type]; 22780 22781 if (handlers) { 22782 // Copy handlers so if handlers are added/removed during the process it doesn't throw everything off. 22783 var handlersCopy = handlers.slice(0); 22784 22785 for (var m = 0, n = handlersCopy.length; m < n; m++) { 22786 if (event.isImmediatePropagationStopped()) { 22787 break; 22788 } else { 22789 try { 22790 handlersCopy[m].call(elem, event, hash); 22791 } catch (e) { 22792 _log2['default'].error(e); 22793 } 22794 } 22795 } 22796 } 22797 }; 22798 } 22799 22800 if (data.handlers[type].length === 1) { 22801 if (elem.addEventListener) { 22802 var options = false; 22803 22804 if (_supportsPassive && passiveEvents.indexOf(type) > -1) { 22805 options = { passive: true }; 22806 } 22807 elem.addEventListener(type, data.dispatcher, options); 22808 } else if (elem.attachEvent) { 22809 elem.attachEvent('on' + type, data.dispatcher); 22810 } 22811 } 22812} 22813 22814/** 22815 * Removes event listeners from an element 22816 * 22817 * @param {Element|Object} elem 22818 * Object to remove listeners from. 22819 * 22820 * @param {string|string[]} [type] 22821 * Type of listener to remove. Don't include to remove all events from element. 22822 * 22823 * @param {EventTarget~EventListener} [fn] 22824 * Specific listener to remove. Don't include to remove listeners for an event 22825 * type. 22826 */ 22827function off(elem, type, fn) { 22828 // Don't want to add a cache object through getElData if not needed 22829 if (!Dom.hasElData(elem)) { 22830 return; 22831 } 22832 22833 var data = Dom.getElData(elem); 22834 22835 // If no events exist, nothing to unbind 22836 if (!data.handlers) { 22837 return; 22838 } 22839 22840 if (Array.isArray(type)) { 22841 return _handleMultipleEvents(off, elem, type, fn); 22842 } 22843 22844 // Utility function 22845 var removeType = function removeType(t) { 22846 data.handlers[t] = []; 22847 _cleanUpEvents(elem, t); 22848 }; 22849 22850 // Are we removing all bound events? 22851 if (!type) { 22852 for (var t in data.handlers) { 22853 removeType(t); 22854 } 22855 return; 22856 } 22857 22858 var handlers = data.handlers[type]; 22859 22860 // If no handlers exist, nothing to unbind 22861 if (!handlers) { 22862 return; 22863 } 22864 22865 // If no listener was provided, remove all listeners for type 22866 if (!fn) { 22867 removeType(type); 22868 return; 22869 } 22870 22871 // We're only removing a single handler 22872 if (fn.guid) { 22873 for (var n = 0; n < handlers.length; n++) { 22874 if (handlers[n].guid === fn.guid) { 22875 handlers.splice(n--, 1); 22876 } 22877 } 22878 } 22879 22880 _cleanUpEvents(elem, type); 22881} 22882 22883/** 22884 * Trigger an event for an element 22885 * 22886 * @param {Element|Object} elem 22887 * Element to trigger an event on 22888 * 22889 * @param {EventTarget~Event|string} event 22890 * A string (the type) or an event object with a type attribute 22891 * 22892 * @param {Object} [hash] 22893 * data hash to pass along with the event 22894 * 22895 * @return {boolean|undefined} 22896 * - Returns the opposite of `defaultPrevented` if default was prevented 22897 * - Otherwise returns undefined 22898 */ 22899function trigger(elem, event, hash) { 22900 // Fetches element data and a reference to the parent (for bubbling). 22901 // Don't want to add a data object to cache for every parent, 22902 // so checking hasElData first. 22903 var elemData = Dom.hasElData(elem) ? Dom.getElData(elem) : {}; 22904 var parent = elem.parentNode || elem.ownerDocument; 22905 // type = event.type || event, 22906 // handler; 22907 22908 // If an event name was passed as a string, creates an event out of it 22909 if (typeof event === 'string') { 22910 event = { type: event, target: elem }; 22911 } 22912 // Normalizes the event properties. 22913 event = fixEvent(event); 22914 22915 // If the passed element has a dispatcher, executes the established handlers. 22916 if (elemData.dispatcher) { 22917 elemData.dispatcher.call(elem, event, hash); 22918 } 22919 22920 // Unless explicitly stopped or the event does not bubble (e.g. media events) 22921 // recursively calls this function to bubble the event up the DOM. 22922 if (parent && !event.isPropagationStopped() && event.bubbles === true) { 22923 trigger.call(null, parent, event, hash); 22924 22925 // If at the top of the DOM, triggers the default action unless disabled. 22926 } else if (!parent && !event.defaultPrevented) { 22927 var targetData = Dom.getElData(event.target); 22928 22929 // Checks if the target has a default action for this event. 22930 if (event.target[event.type]) { 22931 // Temporarily disables event dispatching on the target as we have already executed the handler. 22932 targetData.disabled = true; 22933 // Executes the default action. 22934 if (typeof event.target[event.type] === 'function') { 22935 event.target[event.type](); 22936 } 22937 // Re-enables event dispatching. 22938 targetData.disabled = false;
22939 } 22940 } 22941 22942 // Inform the triggerer if the default was prevented by returning false 22943 return !event.defaultPrevented; 22944} 22945 22946/** 22947 * Trigger a listener only once for an event 22948 * 22949 * @param {Element|Object} elem 22950 * Element or object to bind to. 22951 * 22952 * @param {string|string[]} type 22953 * Name/type of event 22954 * 22955 * @param {Event~EventListener} fn 22956 * Event Listener function 22957 */ 22958function one(elem, type, fn) { 22959 if (Array.isArray(type)) { 22960 return _handleMultipleEvents(one, elem, type, fn); 22961 } 22962 var func = function func() { 22963 off(elem, type, func); 22964 fn.apply(this, arguments); 22965 }; 22966 22967 // copy the guid to the new function so it can removed using the original function's ID 22968 func.guid = fn.guid = fn.guid || Guid.newGUID(); 22969 on(elem, type, func); 22970} 22971 22972},{"81":81,"85":85,"86":86,"94":94,"95":95}],83:[function(_dereq_,module,exports){ 22973'use strict'; 22974 22975exports.__esModule = true; 22976exports.throttle = exports.bind = undefined; 22977 22978var _guid = _dereq_(85); 22979 22980/** 22981 * Bind (a.k.a proxy or Context). A simple method for changing the context of a function 22982 * It also stores a unique id on the function so it can be easily removed from events. 22983 * 22984 * @param {Mixed} context 22985 * The object to bind as scope. 22986 * 22987 * @param {Function} fn 22988 * The function to be bound to a scope. 22989 * 22990 * @param {number} [uid] 22991 * An optional unique ID for the function to be set 22992 * 22993 * @return {Function} 22994 * The new function that will be bound into the context given 22995 */ 22996var bind = exports.bind = function bind(context, fn, uid) { 22997 // Make sure the function has a unique ID 22998 if (!fn.guid) { 22999 fn.guid = (0, _guid.newGUID)(); 23000 } 23001 23002 // Create the new function that changes the context 23003 var bound = function bound() { 23004 return fn.apply(context, arguments); 23005 }; 23006 23007 // Allow for the ability to individualize this function 23008 // Needed in the case where multiple objects might share the same prototype 23009 // IF both items add an event listener with the same function, then you try to remove just one 23010 // it will remove both because they both have the same guid. 23011 // when using this, you need to use the bind method when you remove the listener as well. 23012 // currently used in text tracks 23013 bound.guid = uid ? uid + '_' + fn.guid : fn.guid; 23014 23015 return bound; 23016}; 23017 23018/** 23019 * Wraps the given function, `fn`, with a new function that only invokes `fn` 23020 * at most once per every `wait` milliseconds. 23021 * 23022 * @param {Function} fn 23023 * The function to be throttled. 23024 * 23025 * @param {Number} wait 23026 * The number of milliseconds by which to throttle. 23027 * 23028 * @return {Function} 23029 */ 23030/** 23031 * @file fn.js 23032 * @module fn 23033 */ 23034var throttle = exports.throttle = function throttle(fn, wait) { 23035 var last = Date.now(); 23036 23037 var throttled = function throttled() { 23038 var now = Date.now(); 23039 23040 if (now - last >= wait) { 23041 fn.apply(undefined, arguments); 23042 last = now; 23043 } 23044 }; 23045 23046 return throttled; 23047}; 23048 23049},{"85":85}],84:[function(_dereq_,module,exports){ 23050'use strict'; 23051 23052exports.__esModule = true; 23053/** 23054 * @file format-time.js 23055 * @module Format-time 23056 */ 23057 23058/** 23059 * Format seconds as a time string, H:MM:SS or M:SS. Supplying a guide (in seconds) 23060 * will force a number of leading zeros to cover the length of the guide. 23061 * 23062 * @param {number} seconds 23063 * Number of seconds to be turned into a string 23064 * 23065 * @param {number} guide 23066 * Number (in seconds) to model the string after 23067 * 23068 * @return {string} 23069 * Time formatted as H:MM:SS or M:SS 23070 */ 23071function formatTime(seconds) { 23072 var guide = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : seconds; 23073 23074 seconds = seconds < 0 ? 0 : seconds; 23075 var s = Math.floor(seconds % 60); 23076 var m = Math.floor(seconds / 60 % 60); 23077 var h = Math.floor(seconds / 3600); 23078 var gm = Math.floor(guide / 60 % 60); 23079 var gh = Math.floor(guide / 3600); 23080 23081 // handle invalid times 23082 if (isNaN(seconds) || seconds === Infinity) { 23083 // '-' is false for all relational operators (e.g. <, >=) so this setting 23084 // will add the minimum number of fields specified by the guide 23085 h = m = s = '-'; 23086 } 23087 23088 // Check if we need to show hours 23089 h = h > 0 || gh > 0 ? h + ':' : ''; 23090
23091 // If hours are showing, we may need to add a leading zero. 23092 // Always show at least one digit of minutes. 23093 m = ((h || gm >= 10) && m < 10 ? '0' + m : m) + ':'; 23094 23095 // Check if leading zero is need for seconds 23096 s = s < 10 ? '0' + s : s; 23097 23098 return h + m + s; 23099} 23100 23101exports['default'] = formatTime; 23102 23103},{}],85:[function(_dereq_,module,exports){ 23104"use strict"; 23105 23106exports.__esModule = true; 23107exports.newGUID = newGUID; 23108/** 23109 * @file guid.js 23110 * @module guid 23111 */ 23112 23113/** 23114 * Unique ID for an element or function 23115 * @type {Number} 23116 */ 23117var _guid = 1; 23118 23119/** 23120 * Get a unique auto-incrementing ID by number that has not been returned before. 23121 * 23122 * @return {number} 23123 * A new unique ID. 23124 */ 23125function newGUID() { 23126 return _guid++; 23127} 23128 23129},{}],86:[function(_dereq_,module,exports){ 23130'use strict'; 23131 23132exports.__esModule = true; 23133exports.logByType = undefined; 23134 23135var _window = _dereq_(95); 23136 23137var _window2 = _interopRequireDefault(_window); 23138 23139var _browser = _dereq_(78); 23140 23141var _obj = _dereq_(88); 23142 23143function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 23144 23145var log = void 0; 23146 23147/** 23148 * Log messages to the console and history based on the type of message 23149 * 23150 * @param {string} type 23151 * The name of the console method to use. 23152 * 23153 * @param {Array} args 23154 * The arguments to be passed to the matching console method. 23155 * 23156 * @param {boolean} [stringify] 23157 * By default, only old IEs should get console argument stringification, 23158 * but this is exposed as a parameter to facilitate testing. 23159 */ 23160/** 23161 * @file log.js 23162 * @module log 23163 */ 23164var logByType = exports.logByType = function logByType(type, args) { 23165 var stringify = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : !!_browser.IE_VERSION && _browser.IE_VERSION < 11; 23166 23167 23168 if (type !== 'log') { 23169 23170 // add the type to the front of the message when it's not "log" 23171 args.unshift(type.toUpperCase() + ':'); 23172 } 23173 23174 // add to history 23175 log.history.push(args); 23176 23177 // add console prefix after adding to history 23178 args.unshift('VIDEOJS:'); 23179 23180 // If there's no console then don't try to output messages, but they will 23181 // still be stored in `log.history`. 23182 // 23183 // Was setting these once outside of this function, but containing them 23184 // in the function makes it easier to test cases where console doesn't exist 23185 // when the module is executed. 23186 var fn = _window2['default'].console && _window2['default'].console[type]; 23187 23188 // Bail out if there's no console. 23189 if (!fn) { 23190 return; 23191 } 23192 23193 // IEs previous to 11 log objects uselessly as "[object Object]"; so, JSONify 23194 // objects and arrays for those less-capable browsers. 23195 if (stringify) { 23196 args = args.map(function (a) { 23197 if ((0, _obj.isObject)(a) || Array.isArray(a)) { 23198 try { 23199 return JSON.stringify(a); 23200 } catch (x) { 23201 return String(a); 23202 } 23203 } 23204 23205 // Cast to string before joining, so we get null and undefined explicitly 23206 // included in output (as we would in a modern console). 23207 return String(a); 23208 }).join(' '); 23209 } 23210 23211 // Old IE versions do not allow .apply() for console methods (they are 23212 // reported as objects rather than functions). 23213 if (!fn.apply) { 23214 fn(args); 23215 } else { 23216 fn[Array.isArray(args) ? 'apply' : 'call'](_window2['default'].console, args); 23217 } 23218}; 23219 23220/** 23221 * Log plain debug messages 23222 * 23223 * @param {Mixed[]} args 23224 * One or more messages or objects that should be logged. 23225 */ 23226log = function log() { 23227 for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { 23228 args[_key] = arguments[_key]; 23229 } 23230 23231 logByType('log', args); 23232}; 23233 23234/** 23235 * Keep a history of log messages 23236 * 23237 * @type {Array} 23238 */ 23239log.history = []; 23240 23241/** 23242 * Log error messages 23243 * 23244 * @param {Mixed[]} args 23245 * One or more messages or objects that should be logged as an error 23246 */ 23247log.error = function () { 23248 for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { 23249 args[_key2] = arguments[_key2]; 23250 } 23251 23252 return logByType('error', args); 23253}; 23254 23255/** 23256 * Log warning messages 23257 * 23258 * @param {Mixed[]} args 23259 * One or more messages or objects that should be logged as a warning. 23260 */ 23261log.warn = function () { 23262 for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { 23263 args[_key3] = arguments[_key3]; 23264 } 23265 23266 return logByType('warn', args); 23267}; 23268 23269exports['default'] = log; 23270 23271},{"78":78,"88":88,"95":95}],87:[function(_dereq_,module,exports){ 23272'use strict'; 23273 23274exports.__esModule = true; 23275exports['default'] = mergeOptions; 23276 23277var _obj = _dereq_(88); 23278 23279/** 23280 * Deep-merge one or more options objects, recursively merging **only** plain 23281 * object properties. 23282 * 23283 * @param {Object[]} sources
23284 * One or more objects to merge into a new object. 23285 * 23286 * @returns {Object} 23287 * A new object that is the merged result of all sources. 23288 */ 23289function mergeOptions() { 23290 var result = {}; 23291 23292 for (var _len = arguments.length, sources = Array(_len), _key = 0; _key < _len; _key++) { 23293 sources[_key] = arguments[_key]; 23294 } 23295 23296 sources.forEach(function (source) { 23297 if (!source) { 23298 return; 23299 } 23300 23301 (0, _obj.each)(source, function (value, key) { 23302 if (!(0, _obj.isPlain)(value)) { 23303 result[key] = value; 23304 return; 23305 } 23306 23307 if (!(0, _obj.isPlain)(result[key])) { 23308 result[key] = {}; 23309 } 23310 23311 result[key] = mergeOptions(result[key], value); 23312 }); 23313 }); 23314 23315 return result; 23316} /** 23317 * @file merge-options.js 23318 * @module merge-options 23319 */ 23320 23321},{"88":88}],88:[function(_dereq_,module,exports){ 23322'use strict'; 23323 23324exports.__esModule = true; 23325 23326var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; 23327 23328exports.each = each; 23329exports.reduce = reduce; 23330exports.assign = assign; 23331exports.isObject = isObject; 23332exports.isPlain = isPlain; 23333/** 23334 * @file obj.js 23335 * @module obj 23336 */ 23337 23338/** 23339 * @callback obj:EachCallback 23340 * 23341 * @param {Mixed} value 23342 * The current key for the object that is being iterated over. 23343 * 23344 * @param {string} key 23345 * The current key-value for object that is being iterated over 23346 */ 23347 23348/** 23349 * @callback obj:ReduceCallback 23350 * 23351 * @param {Mixed} accum 23352 * The value that is accumulating over the reduce loop. 23353 * 23354 * @param {Mixed} value 23355 * The current key for the object that is being iterated over. 23356 * 23357 * @param {string} key 23358 * The current key-value for object that is being iterated over 23359 * 23360 * @return {Mixed} 23361 * The new accumulated value. 23362 */ 23363var toString = Object.prototype.toString; 23364 23365/** 23366 * Get the keys of an Object 23367 * 23368 * @param {Object} 23369 * The Object to get the keys from 23370 * 23371 * @return {string[]} 23372 * An array of the keys from the object. Returns an empty array if the 23373 * object passed in was invalid or had no keys. 23374 * 23375 * @private 23376 */ 23377var keys = function keys(object) { 23378 return isObject(object) ? Object.keys(object) : []; 23379}; 23380 23381/** 23382 * Array-like iteration for objects. 23383 * 23384 * @param {Object} object 23385 * The object to iterate over 23386 * 23387 * @param {obj:EachCallback} fn 23388 * The callback function which is called for each key in the object. 23389 */ 23390function each(object, fn) { 23391 keys(object).forEach(function (key) { 23392 return fn(object[key], key); 23393 }); 23394} 23395 23396/** 23397 * Array-like reduce for objects. 23398 * 23399 * @param {Object} object 23400 * The Object that you want to reduce. 23401 * 23402 * @param {Function} fn 23403 * A callback function which is called for each key in the object. It 23404 * receives the accumulated value and the per-iteration value and key 23405 * as arguments. 23406 * 23407 * @param {Mixed} [initial = 0] 23408 * Starting value 23409 * 23410 * @return {Mixed} 23411 * The final accumulated value. 23412 */ 23413function reduce(object, fn) { 23414 var initial = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; 23415 23416 return keys(object).reduce(function (accum, key) { 23417 return fn(accum, object[key], key); 23418 }, initial); 23419} 23420 23421/** 23422 * Object.assign-style object shallow merge/extend. 23423 * 23424 * @param {Object} target 23425 * @param {Object} ...sources 23426 * @return {Object} 23427 */ 23428function assign(target) { 23429 for (var _len = arguments.length, sources = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 23430 sources[_key - 1] = arguments[_key]; 23431 } 23432 23433 if (Object.assign) { 23434 return Object.assign.apply(Object, [target].concat(sources)); 23435 } 23436 23437 sources.forEach(function (source) { 23438 if (!source) { 23439 return; 23440 } 23441 23442 each(source, function (value, key) { 23443 target[key] = value; 23444 }); 23445 }); 23446 23447 return target; 23448} 23449 23450/** 23451 * Returns whether a value is an object of any kind - including DOM nodes, 23452 * arrays, regular expressions, etc. Not functions, though. 23453 * 23454 * This avoids the gotcha where using `typeof` on a `null` value 23455 * results in `'object'`. 23456 * 23457 * @param {Object} value 23458 * @return {Boolean} 23459 */ 23460function isObject(value) { 23461 return !!value && (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object'; 23462} 23463 23464/** 23465 * Returns whether an object appears to be a "plain" object - that is, a
23466 * direct instance of `Object`. 23467 * 23468 * @param {Object} value 23469 * @return {Boolean} 23470 */ 23471function isPlain(value) { 23472 return isObject(value) && toString.call(value) === '[object Object]' && value.constructor === Object; 23473} 23474 23475},{}],89:[function(_dereq_,module,exports){ 23476'use strict'; 23477 23478exports.__esModule = true; 23479exports.setTextContent = exports.createStyleElement = undefined; 23480 23481var _document = _dereq_(94); 23482 23483var _document2 = _interopRequireDefault(_document); 23484 23485function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 23486 23487/** 23488 * Create a DOM syle element given a className for it. 23489 * 23490 * @param {string} className 23491 * The className to add to the created style element. 23492 * 23493 * @return {Element} 23494 * The element that was created. 23495 */ 23496var createStyleElement = exports.createStyleElement = function createStyleElement(className) { 23497 var style = _document2['default'].createElement('style'); 23498 23499 style.className = className; 23500 23501 return style; 23502}; 23503 23504/** 23505 * Add text to a DOM element. 23506 * 23507 * @param {Element} el 23508 * The Element to add text content to. 23509 * 23510 * @param {string} content 23511 * The text to add to the element. 23512 */ 23513/** 23514 * @file stylesheet.js 23515 * @module stylesheet 23516 */ 23517var setTextContent = exports.setTextContent = function setTextContent(el, content) { 23518 if (el.styleSheet) { 23519 el.styleSheet.cssText = content; 23520 } else { 23521 el.textContent = content; 23522 } 23523}; 23524 23525},{"94":94}],90:[function(_dereq_,module,exports){ 23526'use strict'; 23527 23528exports.__esModule = true; 23529exports.createTimeRange = undefined; 23530exports.createTimeRanges = createTimeRanges; 23531 23532var _log = _dereq_(86); 23533 23534var _log2 = _interopRequireDefault(_log); 23535 23536function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 23537 23538/** 23539 * Returns the time for the specified index at the start or end 23540 * of a TimeRange object. 23541 * 23542 * @function time-ranges:indexFunction 23543 * 23544 * @param {number} [index=0] 23545 * The range number to return the time for. 23546 * 23547 * @return {number} 23548 * The time that offset at the specified index. 23549 * 23550 * @depricated index must be set to a value, in the future this will throw an error. 23551 */ 23552 23553/** 23554 * An object that contains ranges of time for various reasons. 23555 * 23556 * @typedef {Object} TimeRange 23557 * 23558 * @property {number} length 23559 * The number of time ranges represented by this Object 23560 * 23561 * @property {time-ranges:indexFunction} start 23562 * Returns the time offset at which a specified time range begins. 23563 * 23564 * @property {time-ranges:indexFunction} end 23565 * Returns the time offset at which a specified time range begins. 23566 * 23567 * @see https://developer.mozilla.org/en-US/docs/Web/API/TimeRanges 23568 */ 23569 23570/** 23571 * Check if any of the time ranges are over the maximum index. 23572 * 23573 * @param {string} fnName 23574 * The function name to use for logging 23575 * 23576 * @param {number} index 23577 * The index to check 23578 * 23579 * @param {number} maxIndex 23580 * The maximum possible index 23581 * 23582 * @throws {Error} if the timeRanges provided are over the maxIndex 23583 */ 23584function rangeCheck(fnName, index, maxIndex) { 23585 if (index < 0 || index > maxIndex) { 23586 throw new Error('Failed to execute \'' + fnName + '\' on \'TimeRanges\': The index provided (' + index + ') is greater than or equal to the maximum bound (' + maxIndex + ').'); 23587 } 23588} 23589 23590/** 23591 * Check if any of the time ranges are over the maximum index. 23592 * 23593 * @param {string} fnName 23594 * The function name to use for logging 23595 * 23596 * @param {string} valueIndex 23597 * The proprety that should be used to get the time. should be 'start' or 'end' 23598 * 23599 * @param {Array} ranges 23600 * An array of time ranges 23601 * 23602 * @param {Array} [rangeIndex=0] 23603 * The index to start the search at 23604 * 23605 * @return {number} 23606 * The time that offset at the specified index. 23607 * 23608 * 23609 * @depricated rangeIndex must be set to a value, in the future this will throw an error. 23610 * @throws {Error} if rangeIndex is more than the length of ranges 23611 */ 23612/** 23613 * @file time-ranges.js 23614 * @module time-ranges 23615 */ 23616function getRange(fnName, valueIndex, ranges, rangeIndex) { 23617 if (rangeIndex === undefined) { 23618 _log2['default'].warn('DEPRECATED: Function \'' + fnName + '\' on \'TimeRanges\' called without an index argument.'); 23619 rangeIndex = 0; 23620 } 23621 rangeCheck(fnName, rangeIndex, ranges.length - 1); 23622 return ranges[rangeIndex][valueIndex]; 23623} 23624 23625/** 23626 * Create a time range object givent ranges of time. 23627 * 23628 * @param {Array} [ranges] 23629 * An array of time ranges. 23630 */ 23631function createTimeRangesObj(ranges) { 23632 if (ranges === undefined || ranges.length === 0) { 23633 return { 23634 length: 0, 23635 start: function start() { 23636 throw new Error('This TimeRanges object is empty'); 23637 }, 23638 end: function end() { 23639 throw new Error('This TimeRanges object is empty'); 23640 } 23641 }; 23642 } 23643 return { 23644 length: ranges.length, 23645 start: getRange.bind(null, 'start', 0, ranges), 23646 end: getRange.bind(null, 'end', 1, ranges) 23647 }; 23648} 23649 23650/** 23651 * Should create a fake `TimeRange` object which mimics an HTML5 time range instance. 23652 * 23653 * @param {number|Array} start 23654 * The start of a single range or an array of ranges 23655 * 23656 * @param {number} end 23657 * The end of a single range. 23658 * 23659 * @private 23660 */ 23661function createTimeRanges(start, end) { 23662 if (Array.isArray(start)) { 23663 return createTimeRangesObj(start); 23664 } else if (start === undefined || end === undefined) { 23665 return createTimeRangesObj(); 23666 } 23667 return createTimeRangesObj([[start, end]]); 23668} 23669 23670exports.createTimeRange = createTimeRanges; 23671 23672},{"86":86}],91:[function(_dereq_,module,exports){ 23673'use strict'; 23674 23675exports.__esModule = true; 23676/** 23677 * @file to-title-case.js 23678 * @module to-title-case 23679 */ 23680 23681/** 23682 * Uppercase the first letter of a string. 23683 * 23684 * @param {string} string 23685 * String to be uppercased 23686 * 23687 * @return {string} 23688 * The string with an uppercased first letter 23689 */ 23690function toTitleCase(string) { 23691 if (typeof string !== 'string') { 23692 return string; 23693 } 23694 23695 return string.charAt(0).toUpperCase() + string.slice(1); 23696} 23697 23698exports['default'] = toTitleCase; 23699 23700},{}],92:[function(_dereq_,module,exports){ 23701'use strict'; 23702 23703exports.__esModule = true; 23704exports.isCrossOrigin = exports.getFileExtension = exports.getAbsoluteURL = exports.parseUrl = undefined; 23705 23706var _document = _dereq_(94); 23707 23708var _document2 = _interopRequireDefault(_document); 23709 23710var _window = _dereq_(95); 23711 23712var _window2 = _interopRequireDefault(_window); 23713 23714function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 23715 23716/** 23717 * @typedef {Object} url:URLObject 23718 * 23719 * @property {string} protocol 23720 * The protocol of the url that was parsed. 23721 * 23722 * @property {string} hostname 23723 * The hostname of the url that was parsed. 23724 * 23725 * @property {string} port 23726 * The port of the url that was parsed. 23727 * 23728 * @property {string} pathname 23729 * The pathname of the url that was parsed. 23730 * 23731 * @property {string} search 23732 * The search query of the url that was parsed. 23733 * 23734 * @property {string} hash 23735 * The hash of the url that was parsed. 23736 * 23737 * @property {string} host 23738 * The host of the url that was parsed. 23739 */ 23740 23741/** 23742 * Resolve and parse the elements of a URL. 23743 * 23744 * @param {String} url 23745 * The url to parse 23746 * 23747 * @return {url:URLObject} 23748 * An object of url details 23749 */ 23750/** 23751 * @file url.js 23752 * @module url 23753 */ 23754var parseUrl = exports.parseUrl = function parseUrl(url) { 23755 var props = ['protocol', 'hostname', 'port', 'pathname', 'search', 'hash', 'host']; 23756 23757 // add the url to an anchor and let the browser parse the URL 23758 var a = _document2['default'].createElement('a'); 23759 23760 a.href = url; 23761 23762 // IE8 (and 9?) Fix
23763 // ie8 doesn't parse the URL correctly until the anchor is actually 23764 // added to the body, and an innerHTML is needed to trigger the parsing 23765 var addToBody = a.host === '' && a.protocol !== 'file:'; 23766 var div = void 0; 23767 23768 if (addToBody) { 23769 div = _document2['default'].createElement('div'); 23770 div.innerHTML = '<a href="' + url + '"></a>'; 23771 a = div.firstChild; 23772 // prevent the div from affecting layout 23773 div.setAttribute('style', 'display:none; position:absolute;'); 23774 _document2['default'].body.appendChild(div); 23775 } 23776 23777 // Copy the specific URL properties to a new object 23778 // This is also needed for IE8 because the anchor loses its 23779 // properties when it's removed from the dom 23780 var details = {}; 23781 23782 for (var i = 0; i < props.length; i++) { 23783 details[props[i]] = a[props[i]]; 23784 } 23785 23786 // IE9 adds the port to the host property unlike everyone else. If 23787 // a port identifier is added for standard ports, strip it. 23788 if (details.protocol === 'http:') { 23789 details.host = details.host.replace(/:80$/, ''); 23790 } 23791 23792 if (details.protocol === 'https:') { 23793 details.host = details.host.replace(/:443$/, ''); 23794 } 23795 23796 if (addToBody) { 23797 _document2['default'].body.removeChild(div); 23798 } 23799 23800 return details; 23801}; 23802 23803/** 23804 * Get absolute version of relative URL. Used to tell flash correct URL. 23805 * 23806 * 23807 * @param {string} url 23808 * URL to make absolute 23809 * 23810 * @return {string} 23811 * Absolute URL 23812 * 23813 * @see http://stackoverflow.com/questions/470832/getting-an-absolute-url-from-a-relative-one-ie6-issue 23814 */ 23815var getAbsoluteURL = exports.getAbsoluteURL = function getAbsoluteURL(url) { 23816 // Check if absolute URL 23817 if (!url.match(/^https?:\/\//)) { 23818 // Convert to absolute URL. Flash hosted off-site needs an absolute URL. 23819 var div = _document2['default'].createElement('div'); 23820 23821 div.innerHTML = '<a href="' + url + '">x</a>'; 23822 url = div.firstChild.href; 23823 } 23824 23825 return url; 23826}; 23827 23828/** 23829 * Returns the extension of the passed file name. It will return an empty string 23830 * if passed an invalid path. 23831 * 23832 * @param {string} path 23833 * The fileName path like '/path/to/file.mp4' 23834 * 23835 * @returns {string} 23836 * The extension in lower case or an empty string if no 23837 * extension could be found. 23838 */ 23839var getFileExtension = exports.getFileExtension = function getFileExtension(path) { 23840 if (typeof path === 'string') { 23841 var splitPathRe = /^(\/?)([\s\S]*?)((?:\.{1,2}|[^\/]+?)(\.([^\.\/\?]+)))(?:[\/]*|[\?].*)$/i; 23842 var pathParts = splitPathRe.exec(path); 23843 23844 if (pathParts) { 23845 return pathParts.pop().toLowerCase(); 23846 } 23847 } 23848 23849 return ''; 23850}; 23851 23852/** 23853 * Returns whether the url passed is a cross domain request or not. 23854 * 23855 * @param {string} url 23856 * The url to check. 23857 * 23858 * @return {boolean} 23859 * Whether it is a cross domain request or not. 23860 */ 23861var isCrossOrigin = exports.isCrossOrigin = function isCrossOrigin(url) { 23862 var winLoc = _window2['default'].location; 23863 var urlInfo = parseUrl(url); 23864 23865 // IE8 protocol relative urls will return ':' for protocol 23866 var srcProtocol = urlInfo.protocol === ':' ? winLoc.protocol : urlInfo.protocol; 23867 23868 // Check if url is for another domain/origin 23869 // IE8 doesn't know location.origin, so we won't rely on it here 23870 var crossOrigin = srcProtocol + urlInfo.host !== winLoc.protocol + winLoc.host; 23871 23872 return crossOrigin; 23873}; 23874 23875},{"94":94,"95":95}],93:[function(_dereq_,module,exports){ 23876'use strict'; 23877 23878exports.__esModule = true; 23879 23880var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; /** 23881 * @file video.js 23882 * @module videojs 23883 */ 23884 23885/* global define */ 23886 23887// Include the built-in techs 23888 23889 23890var _window = _dereq_(95); 23891 23892var _window2 = _interopRequireDefault(_window); 23893 23894var _document = _dereq_(94); 23895 23896var _document2 = _interopRequireDefault(_document); 23897 23898var _browser = _dereq_(78); 23899 23900var browser = _interopRequireWildcard(_browser); 23901 23902var _dom = _dereq_(81); 23903 23904var Dom = _interopRequireWildcard(_dom); 23905 23906var _setup = _dereq_(56); 23907 23908var setup = _interopRequireWildcard(_setup); 23909 23910var _stylesheet = _dereq_(89); 23911 23912var stylesheet = _interopRequireWildcard(_stylesheet); 23913 23914var _component = _dereq_(5); 23915 23916var _component2 = _interopRequireDefault(_component); 23917 23918var _eventTarget = _dereq_(42); 23919 23920var _eventTarget2 = _interopRequireDefault(_eventTarget); 23921 23922var _events = _dereq_(82); 23923 23924var Events = _interopRequireWildcard(_events); 23925 23926var _player = _dereq_(51); 23927 23928var _player2 = _interopRequireDefault(_player); 23929 23930var _plugins = _dereq_(52); 23931 23932var _plugins2 = _interopRequireDefault(_plugins); 23933 23934var _mergeOptions2 = _dereq_(87); 23935 23936var _mergeOptions3 = _interopRequireDefault(_mergeOptions2); 23937 23938var _fn = _dereq_(83); 23939 23940var Fn = _interopRequireWildcard(_fn); 23941 23942var _textTrack = _dereq_(72); 23943 23944var _textTrack2 = _interopRequireDefault(_textTrack); 23945 23946var _audioTrack = _dereq_(64); 23947 23948var _audioTrack2 = _interopRequireDefault(_audioTrack); 23949 23950var _videoTrack = _dereq_(77); 23951 23952var _videoTrack2 = _interopRequireDefault(_videoTrack); 23953 23954var _timeRanges = _dereq_(90); 23955 23956var _formatTime = _dereq_(84); 23957 23958var _formatTime2 = _interopRequireDefault(_formatTime); 23959 23960var _log = _dereq_(86); 23961 23962var _log2 = _interopRequireDefault(_log); 23963 23964var _url = _dereq_(92); 23965 23966var Url = _interopRequireWildcard(_url); 23967 23968var _obj = _dereq_(88); 23969 23970var _computedStyle = _dereq_(80); 23971 23972var _computedStyle2 = _interopRequireDefault(_computedStyle); 23973 23974var _extend = _dereq_(43); 23975 23976var _extend2 = _interopRequireDefault(_extend); 23977 23978var _xhr = _dereq_(104); 23979
23980var _xhr2 = _interopRequireDefault(_xhr); 23981 23982var _tech = _dereq_(62); 23983 23984var _tech2 = _interopRequireDefault(_tech); 23985 23986function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } 23987 23988function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 23989 23990// HTML5 Element Shim for IE8 23991if (typeof HTMLVideoElement === 'undefined' && Dom.isReal()) { 23992 _document2['default'].createElement('video'); 23993 _document2['default'].createElement('audio'); 23994 _document2['default'].createElement('track'); 23995} 23996 23997/** 23998 * Doubles as the main function for users to create a player instance and also 23999 * the main library object. 24000 * The `videojs` function can be used to initialize or retrieve a player. 24001 * 24002 * @param {string|Element} id 24003 * Video element or video element ID 24004 * 24005 * @param {Object} [options] 24006 * Optional options object for config/settings 24007 * 24008 * @param {Component~ReadyCallback} [ready] 24009 * Optional ready callback 24010 * 24011 * @return {Player} 24012 * A player instance 24013 * 24014 * @mixes videojs 24015 */ 24016function videojs(id, options, ready) { 24017 var tag = void 0; 24018 24019 // Allow for element or ID to be passed in 24020 // String ID 24021 if (typeof id === 'string') { 24022 24023 // Adjust for jQuery ID syntax 24024 if (id.indexOf('#') === 0) { 24025 id = id.slice(1); 24026 } 24027 24028 // If a player instance has already been created for this ID return it. 24029 if (videojs.getPlayers()[id]) { 24030 24031 // If options or ready funtion are passed, warn 24032 if (options) { 24033 _log2['default'].warn('Player "' + id + '" is already initialised. Options will not be applied.'); 24034 } 24035 24036 if (ready) { 24037 videojs.getPlayers()[id].ready(ready); 24038 } 24039 24040 return videojs.getPlayers()[id]; 24041 } 24042 24043 // Otherwise get element for ID 24044 tag = Dom.getEl(id); 24045 24046 // ID is a media element 24047 } else { 24048 tag = id; 24049 } 24050 24051 // Check for a useable element 24052 // re: nodeName, could be a box div also 24053 if (!tag || !tag.nodeName) { 24054 throw new TypeError('The element or ID supplied is not valid. (videojs)'); 24055 } 24056 24057 // Element may have a player attr referring to an already created player instance. 24058 // If so return that otherwise set up a new player below 24059 if (tag.player || _player2['default'].players[tag.playerId]) { 24060 return tag.player || _player2['default'].players[tag.playerId]; 24061 } 24062 24063 options = options || {}; 24064 24065 videojs.hooks('beforesetup').forEach(function (hookFunction) { 24066 var opts = hookFunction(tag, (0, _mergeOptions3['default'])(options)); 24067 24068 if (!(0, _obj.isObject)(opts) || Array.isArray(opts)) { 24069 _log2['default'].error('please return an object in beforesetup hooks'); 24070 return; 24071 } 24072 24073 options = (0, _mergeOptions3['default'])(options, opts); 24074 }); 24075 24076 var PlayerComponent = _component2['default'].getComponent('Player'); 24077 // If not, set up a new player 24078 var player = new PlayerComponent(tag, options, ready); 24079 24080 videojs.hooks('setup').forEach(function (hookFunction) { 24081 return hookFunction(player); 24082 }); 24083 24084 return player; 24085} 24086 24087/** 24088 * An Object that contains lifecycle hooks as keys which point to an array 24089 * of functions that are run when a lifecycle is triggered 24090 */ 24091videojs.hooks_ = {}; 24092 24093/** 24094 * Get a list of hooks for a specific lifecycle 24095 * 24096 * @param {string} type 24097 * the lifecyle to get hooks from 24098 * 24099 * @param {Function} [fn] 24100 * Optionally add a hook to the lifecycle that your are getting. 24101 * 24102 * @return {Array} 24103 * an array of hooks, or an empty array if there are none. 24104 */ 24105videojs.hooks = function (type, fn) { 24106 videojs.hooks_[type] = videojs.hooks_[type] || []; 24107 if (fn) { 24108 videojs.hooks_[type] = videojs.hooks_[type].concat(fn); 24109 } 24110 return videojs.hooks_[type]; 24111}; 24112 24113/** 24114 * Add a function hook to a specific videojs lifecycle. 24115 * 24116 * @param {string} type 24117 * the lifecycle to hook the function to. 24118 * 24119 * @param {Function|Function[]} 24120 * The function or array of functions to attach. 24121 */
24122videojs.hook = function (type, fn) { 24123 videojs.hooks(type, fn); 24124}; 24125 24126/** 24127 * Remove a hook from a specific videojs lifecycle. 24128 * 24129 * @param {string} type 24130 * the lifecycle that the function hooked to 24131 * 24132 * @param {Function} fn 24133 * The hooked function to remove 24134 * 24135 * @return {boolean} 24136 * The function that was removed or undef 24137 */ 24138videojs.removeHook = function (type, fn) { 24139 var index = videojs.hooks(type).indexOf(fn); 24140 24141 if (index <= -1) { 24142 return false; 24143 } 24144 24145 videojs.hooks_[type] = videojs.hooks_[type].slice(); 24146 videojs.hooks_[type].splice(index, 1); 24147 24148 return true; 24149}; 24150 24151// Add default styles 24152if (_window2['default'].VIDEOJS_NO_DYNAMIC_STYLE !== true && Dom.isReal()) { 24153 var style = Dom.$('.vjs-styles-defaults'); 24154 24155 if (!style) { 24156 style = stylesheet.createStyleElement('vjs-styles-defaults'); 24157 var head = Dom.$('head'); 24158 24159 if (head) { 24160 head.insertBefore(style, head.firstChild); 24161 } 24162 stylesheet.setTextContent(style, '\n .video-js {\n width: 300px;\n height: 150px;\n }\n\n .vjs-fluid {\n padding-top: 56.25%\n }\n '); 24163 } 24164} 24165 24166// Run Auto-load players 24167// You have to wait at least once in case this script is loaded after your 24168// video in the DOM (weird behavior only with minified version) 24169setup.autoSetupTimeout(1, videojs); 24170 24171/** 24172 * Current software version. Follows semver. 24173 * 24174 * @type {string} 24175 */ 24176videojs.VERSION = '5.20.2'; 24177 24178/** 24179 * The global options object. These are the settings that take effect 24180 * if no overrides are specified when the player is created. 24181 * 24182 * @type {Object} 24183 */ 24184videojs.options = _player2['default'].prototype.options_; 24185 24186/** 24187 * Get an object with the currently created players, keyed by player ID 24188 * 24189 * @return {Object} 24190 * The created players 24191 */ 24192videojs.getPlayers = function () { 24193 return _player2['default'].players; 24194}; 24195 24196/** 24197 * Expose players object. 24198 * 24199 * @memberOf videojs 24200 * @property {Object} players 24201 */ 24202videojs.players = _player2['default'].players; 24203 24204/** 24205 * Get a component class object by name 24206 * 24207 * @borrows Component.getComponent as videojs.getComponent 24208 */ 24209videojs.getComponent = _component2['default'].getComponent; 24210 24211/** 24212 * Register a component so it can referred to by name. Used when adding to other 24213 * components, either through addChild `component.addChild('myComponent')` or through 24214 * default children options `{ children: ['myComponent'] }`. 24215 * 24216 * > NOTE: You could also just initialize the component before adding. 24217 * `component.addChild(new MyComponent());` 24218 * 24219 * @param {string} name 24220 * The class name of the component 24221 * 24222 * @param {Component} comp 24223 * The component class 24224 * 24225 * @return {Component} 24226 * The newly registered component 24227 */ 24228videojs.registerComponent = function (name, comp) { 24229 if (_tech2['default'].isTech(comp)) { 24230 _log2['default'].warn('The ' + name + ' tech was registered as a component. It should instead be registered using videojs.registerTech(name, tech)'); 24231 } 24232 24233 _component2['default'].registerComponent.call(_component2['default'], name, comp); 24234}; 24235 24236/** 24237 * Get a Tech class object by name 24238 * 24239 * @borrows Tech.getTech as videojs.getTech 24240 */ 24241videojs.getTech = _tech2['default'].getTech; 24242 24243/** 24244 * Register a Tech so it can referred to by name. 24245 * This is used in the tech order for the player. 24246 * 24247 * @borrows Tech.registerTech as videojs.registerTech 24248 */ 24249videojs.registerTech = _tech2['default'].registerTech; 24250 24251/** 24252 * A suite of browser and device tests from {@link browser}. 24253 * 24254 * @type {Object} 24255 * @private 24256 */ 24257videojs.browser = browser; 24258 24259/** 24260 * Whether or not the browser supports touch events. Included for backward 24261 * compatibility with 4.x, but deprecated. Use `videojs.browser.TOUCH_ENABLED` 24262 * instead going forward. 24263 * 24264 * @deprecated since version 5.0 24265 * @type {boolean} 24266 */ 24267videojs.TOUCH_ENABLED = browser.TOUCH_ENABLED; 24268 24269/** 24270 * Subclass an existing class 24271 * Mimics ES6 subclassing with the `extend` keyword 24272 * 24273 * @borrows extend:extendFn as videojs.extend 24274 */ 24275videojs.extend = _extend2['default']; 24276 24277/** 24278 * Merge two options objects recursively 24279 * Performs a deep merge like lodash.merge but **only merges plain objects** 24280 * (not arrays, elements, anything else) 24281 * Other values will be copied directly from the second object. 24282 * 24283 * @borrows merge-options:mergeOptions as videojs.mergeOptions 24284 */ 24285videojs.mergeOptions = _mergeOptions3['default']; 24286 24287/** 24288 * Change the context (this) of a function 24289 * 24290 * > NOTE: as of v5.0 we require an ES5 shim, so you should use the native 24291 * `function() {}.bind(newContext);` instead of this. 24292 * 24293 * @borrows fn:bind as videojs.bind 24294 */ 24295videojs.bind = Fn.bind; 24296 24297/** 24298 * Create a Video.js player plugin. 24299 * Plugins are only initialized when options for the plugin are included 24300 * in the player options, or the plugin function on the player instance is 24301 * called. 24302 * 24303 * @borrows plugin:plugin as videojs.plugin 24304 */ 24305videojs.plugin = _plugins2['default']; 24306 24307/** 24308 * Adding languages so that they're available to all players. 24309 * Example: `videojs.addLanguage('es', { 'Hello': 'Hola' });` 24310 * 24311 * @param {string} code 24312 * The language code or dictionary property 24313 * 24314 * @param {Object} data 24315 * The data values to be translated 24316 * 24317 * @return {Object} 24318 * The resulting language dictionary object 24319 */ 24320videojs.addLanguage = function (code, data) { 24321 var _mergeOptions; 24322 24323 code = ('' + code).toLowerCase(); 24324 24325 videojs.options.languages = (0, _mergeOptions3['default'])(videojs.options.languages, (_mergeOptions = {}, _mergeOptions[code] = data, _mergeOptions)); 24326 24327 return videojs.options.languages[code]; 24328}; 24329 24330/** 24331 * Log messages 24332 * 24333 * @borrows log:log as videojs.log 24334 */ 24335videojs.log = _log2['default']; 24336 24337/** 24338 * Creates an emulated TimeRange object. 24339 * 24340 * @borrows time-ranges:createTimeRanges as videojs.createTimeRange 24341 */ 24342/** 24343 * @borrows time-ranges:createTimeRanges as videojs.createTimeRanges 24344 */ 24345videojs.createTimeRange = videojs.createTimeRanges = _timeRanges.createTimeRanges; 24346 24347/** 24348 * Format seconds as a time string, H:MM:SS or M:SS 24349 * Supplying a guide (in seconds) will force a number of leading zeros 24350 * to cover the length of the guide 24351 * 24352 * @borrows format-time:formatTime as videojs.formatTime 24353 */ 24354videojs.formatTime = _formatTime2['default']; 24355 24356/** 24357 * Resolve and parse the elements of a URL 24358 * 24359 * @borrows url:parseUrl as videojs.parseUrl 24360 */ 24361videojs.parseUrl = Url.parseUrl; 24362 24363/** 24364 * Returns whether the url passed is a cross domain request or not. 24365 * 24366 * @borrows url:isCrossOrigin as videojs.isCrossOrigin 24367 */
24368videojs.isCrossOrigin = Url.isCrossOrigin; 24369 24370/** 24371 * Event target class. 24372 * 24373 * @borrows EventTarget as videojs.EventTarget 24374 */ 24375videojs.EventTarget = _eventTarget2['default']; 24376 24377/** 24378 * Add an event listener to element 24379 * It stores the handler function in a separate cache object 24380 * and adds a generic handler to the element's event, 24381 * along with a unique id (guid) to the element. 24382 * 24383 * @borrows events:on as videojs.on 24384 */ 24385videojs.on = Events.on; 24386 24387/** 24388 * Trigger a listener only once for an event 24389 * 24390 * @borrows events:one as videojs.one 24391 */ 24392videojs.one = Events.one; 24393 24394/** 24395 * Removes event listeners from an element 24396 * 24397 * @borrows events:off as videojs.off 24398 */ 24399videojs.off = Events.off; 24400 24401/** 24402 * Trigger an event for an element 24403 * 24404 * @borrows events:trigger as videojs.trigger 24405 */ 24406videojs.trigger = Events.trigger; 24407 24408/** 24409 * A cross-browser XMLHttpRequest wrapper. Here's a simple example: 24410 * 24411 * @param {Object} options 24412 * settings for the request. 24413 * 24414 * @return {XMLHttpRequest|XDomainRequest} 24415 * The request object. 24416 * 24417 * @see https://github.com/Raynos/xhr 24418 */ 24419videojs.xhr = _xhr2['default']; 24420 24421/** 24422 * TextTrack class 24423 * 24424 * @borrows TextTrack as videojs.TextTrack 24425 */ 24426videojs.TextTrack = _textTrack2['default']; 24427 24428/** 24429 * export the AudioTrack class so that source handlers can create 24430 * AudioTracks and then add them to the players AudioTrackList 24431 * 24432 * @borrows AudioTrack as videojs.AudioTrack 24433 */ 24434videojs.AudioTrack = _audioTrack2['default']; 24435 24436/** 24437 * export the VideoTrack class so that source handlers can create 24438 * VideoTracks and then add them to the players VideoTrackList 24439 * 24440 * @borrows VideoTrack as videojs.VideoTrack 24441 */ 24442videojs.VideoTrack = _videoTrack2['default']; 24443 24444/** 24445 * Determines, via duck typing, whether or not a value is a DOM element. 24446 * 24447 * @borrows dom:isEl as videojs.isEl 24448 */ 24449videojs.isEl = Dom.isEl; 24450 24451/** 24452 * Determines, via duck typing, whether or not a value is a text node. 24453 * 24454 * @borrows dom:isTextNode as videojs.isTextNode 24455 */ 24456videojs.isTextNode = Dom.isTextNode; 24457 24458/** 24459 * Creates an element and applies properties. 24460 * 24461 * @borrows dom:createEl as videojs.createEl 24462 */ 24463videojs.createEl = Dom.createEl; 24464 24465/** 24466 * Check if an element has a CSS class 24467 * 24468 * @borrows dom:hasElClass as videojs.hasClass 24469 */ 24470videojs.hasClass = Dom.hasElClass; 24471 24472/** 24473 * Add a CSS class name to an element 24474 * 24475 * @borrows dom:addElClass as videojs.addClass 24476 */ 24477videojs.addClass = Dom.addElClass; 24478 24479/** 24480 * Remove a CSS class name from an element 24481 * 24482 * @borrows dom:removeElClass as videojs.removeClass 24483 */ 24484videojs.removeClass = Dom.removeElClass; 24485 24486/** 24487 * Adds or removes a CSS class name on an element depending on an optional 24488 * condition or the presence/absence of the class name. 24489 * 24490 * @borrows dom:toggleElClass as videojs.toggleClass 24491 */ 24492videojs.toggleClass = Dom.toggleElClass; 24493 24494/** 24495 * Apply attributes to an HTML element. 24496 * 24497 * @borrows dom:setElAttributes as videojs.setAttribute 24498 */ 24499videojs.setAttributes = Dom.setElAttributes; 24500 24501/** 24502 * Get an element's attribute values, as defined on the HTML tag 24503 * Attributes are not the same as properties. They're defined on the tag 24504 * or with setAttribute (which shouldn't be used with HTML) 24505 * This will return true or false for boolean attributes. 24506 * 24507 * @borrows dom:getElAttributes as videojs.getAttributes 24508 */ 24509videojs.getAttributes = Dom.getElAttributes; 24510 24511/** 24512 * Empties the contents of an element. 24513 * 24514 * @borrows dom:emptyEl as videojs.emptyEl 24515 */ 24516videojs.emptyEl = Dom.emptyEl; 24517 24518/** 24519 * Normalizes and appends content to an element. 24520 * 24521 * The content for an element can be passed in multiple types and 24522 * combinations, whose behavior is as follows: 24523 * 24524 * - String 24525 * Normalized into a text node. 24526 * 24527 * - Element, TextNode 24528 * Passed through. 24529 * 24530 * - Array 24531 * A one-dimensional array of strings, elements, nodes, or functions (which 24532 * return single strings, elements, or nodes). 24533 * 24534 * - Function 24535 * If the sole argument, is expected to produce a string, element, 24536 * node, or array. 24537 * 24538 * @borrows dom:appendContents as videojs.appendContet 24539 */ 24540videojs.appendContent = Dom.appendContent; 24541 24542/** 24543 * Normalizes and inserts content into an element; this is identical to 24544 * `appendContent()`, except it empties the element first. 24545 * 24546 * The content for an element can be passed in multiple types and 24547 * combinations, whose behavior is as follows: 24548 * 24549 * - String 24550 * Normalized into a text node. 24551 * 24552 * - Element, TextNode 24553 * Passed through. 24554 * 24555 * - Array
24556 * A one-dimensional array of strings, elements, nodes, or functions (which 24557 * return single strings, elements, or nodes). 24558 * 24559 * - Function 24560 * If the sole argument, is expected to produce a string, element, 24561 * node, or array. 24562 * 24563 * @borrows dom:insertContent as videojs.insertContent 24564 */ 24565videojs.insertContent = Dom.insertContent; 24566 24567/** 24568 * A safe getComputedStyle with an IE8 fallback. 24569 * 24570 * This is because in Firefox, if the player is loaded in an iframe with `display:none`, 24571 * then `getComputedStyle` returns `null`, so, we do a null-check to make sure 24572 * that the player doesn't break in these cases. 24573 * See https://bugzilla.mozilla.org/show_bug.cgi?id=548397 for more details. 24574 * 24575 * @borrows computed-style:computedStyle as videojs.computedStyle 24576 */ 24577videojs.computedStyle = _computedStyle2['default']; 24578 24579/* 24580 * Custom Universal Module Definition (UMD) 24581 * 24582 * Video.js will never be a non-browser lib so we can simplify UMD a bunch and 24583 * still support requirejs and browserify. This also needs to be closure 24584 * compiler compatible, so string keys are used. 24585 */ 24586if (typeof define === 'function' && define.amd) { 24587 define('videojs', [], function () { 24588 return videojs; 24589 }); 24590 24591 // checking that module is an object too because of umdjs/umd#35 24592} else if ((typeof exports === 'undefined' ? 'undefined' : _typeof(exports)) === 'object' && (typeof module === 'undefined' ? 'undefined' : _typeof(module)) === 'object') { 24593 module.exports = videojs; 24594} 24595 24596exports['default'] = videojs; 24597 24598},{"104":104,"42":42,"43":43,"5":5,"51":51,"52":52,"56":56,"62":62,"64":64,"72":72,"77":77,"78":78,"80":80,"81":81,"82":82,"83":83,"84":84,"86":86,"87":87,"88":88,"89":89,"90":90,"92":92,"94":94,"95":95}],94:[function(_dereq_,module,exports){ 24599(function (global){ 24600var topLevel = typeof global !== 'undefined' ? global : 24601 typeof window !== 'undefined' ? window : {} 24602var minDoc = _dereq_(96); 24603 24604if (typeof document !== 'undefined') { 24605 module.exports = document; 24606} else { 24607 var doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4']; 24608 24609 if (!doccy) { 24610 doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc; 24611 } 24612 24613 module.exports = doccy; 24614} 24615 24616}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 24617},{"96":96}],95:[function(_dereq_,module,exports){ 24618(function (global){ 24619if (typeof window !== "undefined") { 24620 module.exports = window; 24621} else if (typeof global !== "undefined") { 24622 module.exports = global; 24623} else if (typeof self !== "undefined"){ 24624 module.exports = self; 24625} else { 24626 module.exports = {}; 24627} 24628 24629}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 24630},{}],96:[function(_dereq_,module,exports){ 24631 24632},{}],97:[function(_dereq_,module,exports){ 24633module.exports = SafeParseTuple 24634 24635function SafeParseTuple(obj, reviver) { 24636 var json 24637 var error = null 24638 24639 try { 24640 json = JSON.parse(obj, reviver) 24641 } catch (err) { 24642 error = err 24643 } 24644 24645 return [error, json] 24646} 24647 24648},{}],98:[function(_dereq_,module,exports){ 24649function clean (s) { 24650 return s.replace(/\n\r?\s*/g, '') 24651} 24652 24653 24654module.exports = function tsml (sa) { 24655 var s = '' 24656 , i = 0 24657 24658 for (; i < arguments.length; i++) 24659 s += clean(sa[i]) + (arguments[i + 1] || '') 24660 24661 return s 24662} 24663},{}],99:[function(_dereq_,module,exports){ 24664/** 24665 * Copyright 2013 vtt.js Contributors 24666 * 24667 * Licensed under the Apache License, Version 2.0 (the "License"); 24668 * you may not use this file except in compliance with the License. 24669 * You may obtain a copy of the License at 24670 * 24671 * http://www.apache.org/licenses/LICENSE-2.0 24672 * 24673 * Unless required by applicable law or agreed to in writing, software 24674 * distributed under the License is distributed on an "AS IS" BASIS, 24675 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24676 * See the License for the specific language governing permissions and 24677 * limitations under the License. 24678 */ 24679 24680// Default exports for Node. Export the extended versions of VTTCue and 24681// VTTRegion in Node since we likely want the capability to convert back and 24682// forth between JSON. If we don't then it's not that big of a deal since we're 24683// off browser. 24684 24685var window = _dereq_(103); 24686 24687var vttjs = module.exports = { 24688 WebVTT: _dereq_(100), 24689 VTTCue: _dereq_(101), 24690 VTTRegion: _dereq_(102) 24691}; 24692 24693window.vttjs = vttjs; 24694window.WebVTT = vttjs.WebVTT; 24695 24696var cueShim = vttjs.VTTCue; 24697var regionShim = vttjs.VTTRegion; 24698var nativeVTTCue = window.VTTCue; 24699var nativeVTTRegion = window.VTTRegion; 24700 24701vttjs.shim = function() { 24702 window.VTTCue = cueShim; 24703 window.VTTRegion = regionShim; 24704}; 24705 24706vttjs.restore = function() { 24707 window.VTTCue = nativeVTTCue; 24708 window.VTTRegion = nativeVTTRegion; 24709}; 24710 24711if (!window.VTTCue) { 24712 vttjs.shim(); 24713} 24714 24715},{"100":100,"101":101,"102":102,"103":103}],100:[function(_dereq_,module,exports){ 24716/** 24717 * Copyright 2013 vtt.js Contributors 24718 * 24719 * Licensed under the Apache License, Version 2.0 (the "License");
vendor: 16,438 bytes, lines 24720-25257
24720 * you may not use this file except in compliance with the License. 24721 * You may obtain a copy of the License at 24722 * 24723 * http://www.apache.org/licenses/LICENSE-2.0 24724 * 24725 * Unless required by applicable law or agreed to in writing, software 24726 * distributed under the License is distributed on an "AS IS" BASIS, 24727 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24728 * See the License for the specific language governing permissions and 24729 * limitations under the License. 24730 */ 24731 24732/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 24733/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ 24734var _objCreate = Object.create || (function() { 24735 function F() {} 24736 return function(o) { 24737 if (arguments.length !== 1) { 24738 throw new Error('Object.create shim only accepts one parameter.'); 24739 } 24740 F.prototype = o; 24741 return new F(); 24742 }; 24743})(); 24744 24745// Creates a new ParserError object from an errorData object. The errorData 24746// object should have default code and message properties. The default message 24747// property can be overriden by passing in a message parameter. 24748// See ParsingError.Errors below for acceptable errors. 24749function ParsingError(errorData, message) { 24750 this.name = "ParsingError"; 24751 this.code = errorData.code; 24752 this.message = message || errorData.message; 24753} 24754ParsingError.prototype = _objCreate(Error.prototype); 24755ParsingError.prototype.constructor = ParsingError; 24756 24757// ParsingError metadata for acceptable ParsingErrors. 24758ParsingError.Errors = { 24759 BadSignature: { 24760 code: 0, 24761 message: "Malformed WebVTT signature." 24762 }, 24763 BadTimeStamp: { 24764 code: 1, 24765 message: "Malformed time stamp." 24766 } 24767}; 24768 24769// Try to parse input as a time stamp. 24770function parseTimeStamp(input) { 24771 24772 function computeSeconds(h, m, s, f) { 24773 return (h | 0) * 3600 + (m | 0) * 60 + (s | 0) + (f | 0) / 1000; 24774 } 24775 24776 var m = input.match(/^(\d+):(\d{2})(:\d{2})?\.(\d{3})/); 24777 if (!m) { 24778 return null; 24779 } 24780 24781 if (m[3]) { 24782 // Timestamp takes the form of [hours]:[minutes]:[seconds].[milliseconds] 24783 return computeSeconds(m[1], m[2], m[3].replace(":", ""), m[4]); 24784 } else if (m[1] > 59) { 24785 // Timestamp takes the form of [hours]:[minutes].[milliseconds] 24786 // First position is hours as it's over 59. 24787 return computeSeconds(m[1], m[2], 0, m[4]); 24788 } else { 24789 // Timestamp takes the form of [minutes]:[seconds].[milliseconds] 24790 return computeSeconds(0, m[1], m[2], m[4]); 24791 } 24792} 24793 24794// A settings object holds key/value pairs and will ignore anything but the first 24795// assignment to a specific key. 24796function Settings() { 24797 this.values = _objCreate(null); 24798} 24799 24800Settings.prototype = { 24801 // Only accept the first assignment to any key. 24802 set: function(k, v) { 24803 if (!this.get(k) && v !== "") { 24804 this.values[k] = v; 24805 } 24806 }, 24807 // Return the value for a key, or a default value. 24808 // If 'defaultKey' is passed then 'dflt' is assumed to be an object with 24809 // a number of possible default values as properties where 'defaultKey' is 24810 // the key of the property that will be chosen; otherwise it's assumed to be 24811 // a single value. 24812 get: function(k, dflt, defaultKey) { 24813 if (defaultKey) { 24814 return this.has(k) ? this.values[k] : dflt[defaultKey]; 24815 } 24816 return this.has(k) ? this.values[k] : dflt; 24817 }, 24818 // Check whether we have a value for a key. 24819 has: function(k) { 24820 return k in this.values; 24821 }, 24822 // Accept a setting if its one of the given alternatives. 24823 alt: function(k, v, a) { 24824 for (var n = 0; n < a.length; ++n) { 24825 if (v === a[n]) { 24826 this.set(k, v); 24827 break; 24828 } 24829 } 24830 }, 24831 // Accept a setting if its a valid (signed) integer. 24832 integer: function(k, v) { 24833 if (/^-?\d+$/.test(v)) { // integer 24834 this.set(k, parseInt(v, 10)); 24835 } 24836 }, 24837 // Accept a setting if its a valid percentage. 24838 percent: function(k, v) { 24839 var m; 24840 if ((m = v.match(/^([\d]{1,3})(\.[\d]*)?%$/))) { 24841 v = parseFloat(v); 24842 if (v >= 0 && v <= 100) { 24843 this.set(k, v); 24844 return true; 24845 } 24846 } 24847 return false; 24848 } 24849}; 24850 24851// Helper function to parse input into groups separated by 'groupDelim', and 24852// interprete each group as a key/value pair separated by 'keyValueDelim'. 24853function parseOptions(input, callback, keyValueDelim, groupDelim) { 24854 var groups = groupDelim ? input.split(groupDelim) : [input]; 24855 for (var i in groups) { 24856 if (typeof groups[i] !== "string") { 24857 continue; 24858 } 24859 var kv = groups[i].split(keyValueDelim); 24860 if (kv.length !== 2) { 24861 continue; 24862 } 24863 var k = kv[0]; 24864 var v = kv[1]; 24865 callback(k, v); 24866 } 24867} 24868 24869function parseCue(input, cue, regionList) { 24870 // Remember the original input if we need to throw an error. 24871 var oInput = input; 24872 // 4.1 WebVTT timestamp 24873 function consumeTimeStamp() { 24874 var ts = parseTimeStamp(input); 24875 if (ts === null) { 24876 throw new ParsingError(ParsingError.Errors.BadTimeStamp, 24877 "Malformed timestamp: " + oInput); 24878 } 24879 // Remove time stamp from input. 24880 input = input.replace(/^[^\sa-zA-Z-]+/, ""); 24881 return ts; 24882 } 24883 24884 // 4.4.2 WebVTT cue settings 24885 function consumeCueSettings(input, cue) { 24886 var settings = new Settings(); 24887 24888 parseOptions(input, function (k, v) { 24889 switch (k) { 24890 case "region": 24891 // Find the last region we parsed with the same region id. 24892 for (var i = regionList.length - 1; i >= 0; i--) { 24893 if (regionList[i].id === v) { 24894 settings.set(k, regionList[i].region); 24895 break; 24896 } 24897 } 24898 break; 24899 case "vertical": 24900 settings.alt(k, v, ["rl", "lr"]); 24901 break; 24902 case "line": 24903 var vals = v.split(","), 24904 vals0 = vals[0]; 24905 settings.integer(k, vals0); 24906 settings.percent(k, vals0) ? settings.set("snapToLines", false) : null; 24907 settings.alt(k, vals0, ["auto"]); 24908 if (vals.length === 2) { 24909 settings.alt("lineAlign", vals[1], ["start", "middle", "end"]); 24910 } 24911 break; 24912 case "position": 24913 vals = v.split(","); 24914 settings.percent(k, vals[0]); 24915 if (vals.length === 2) { 24916 settings.alt("positionAlign", vals[1], ["start", "middle", "end"]); 24917 } 24918 break; 24919 case "size": 24920 settings.percent(k, v); 24921 break; 24922 case "align": 24923 settings.alt(k, v, ["start", "middle", "end", "left", "right"]); 24924 break; 24925 } 24926 }, /:/, /\s/); 24927 24928 // Apply default values for any missing fields. 24929 cue.region = settings.get("region", null); 24930 cue.vertical = settings.get("vertical", ""); 24931 cue.line = settings.get("line", "auto"); 24932 cue.lineAlign = settings.get("lineAlign", "start"); 24933 cue.snapToLines = settings.get("snapToLines", true); 24934 cue.size = settings.get("size", 100); 24935 cue.align = settings.get("align", "middle"); 24936 cue.position = settings.get("position", { 24937 start: 0, 24938 left: 0, 24939 middle: 50, 24940 end: 100, 24941 right: 100 24942 }, cue.align); 24943 cue.positionAlign = settings.get("positionAlign", { 24944 start: "start", 24945 left: "start", 24946 middle: "middle", 24947 end: "end", 24948 right: "end" 24949 }, cue.align); 24950 } 24951 24952 function skipWhitespace() { 24953 input = input.replace(/^\s+/, ""); 24954 } 24955 24956 // 4.1 WebVTT cue timings. 24957 skipWhitespace(); 24958 cue.startTime = consumeTimeStamp(); // (1) collect cue start time 24959 skipWhitespace(); 24960 if (input.substr(0, 3) !== "-->") { // (3) next characters must match "-->" 24961 throw new ParsingError(ParsingError.Errors.BadTimeStamp, 24962 "Malformed time stamp (time stamps must be separated by '-->'): " + 24963 oInput); 24964 } 24965 input = input.substr(3); 24966 skipWhitespace(); 24967 cue.endTime = consumeTimeStamp(); // (5) collect cue end time 24968 24969 // 4.1 WebVTT cue settings list. 24970 skipWhitespace(); 24971 consumeCueSettings(input, cue); 24972} 24973 24974var ESCAPE = { 24975 "&": "&", 24976 "<": "<", 24977 ">": ">", 24978 "‎": "\u200e", 24979 "‏": "\u200f", 24980 " ": "\u00a0" 24981}; 24982 24983var TAG_NAME = { 24984 c: "span", 24985 i: "i", 24986 b: "b", 24987 u: "u", 24988 ruby: "ruby", 24989 rt: "rt", 24990 v: "span", 24991 lang: "span" 24992}; 24993 24994var TAG_ANNOTATION = { 24995 v: "title", 24996 lang: "lang" 24997}; 24998 24999var NEEDS_PARENT = { 25000 rt: "ruby" 25001}; 25002 25003// Parse content into a document fragment. 25004function parseContent(window, input) { 25005 function nextToken() { 25006 // Check for end-of-string. 25007 if (!input) { 25008 return null; 25009 } 25010 25011 // Consume 'n' characters from the input. 25012 function consume(result) { 25013 input = input.substr(result.length); 25014 return result; 25015 } 25016 25017 var m = input.match(/^([^<]*)(<[^>]+>?)?/); 25018 // If there is some text before the next tag, return it, otherwise return 25019 // the tag. 25020 return consume(m[1] ? m[1] : m[2]); 25021 } 25022 25023 // Unescape a string 's'. 25024 function unescape1(e) { 25025 return ESCAPE[e]; 25026 } 25027 function unescape(s) { 25028 while ((m = s.match(/&(amp|lt|gt|lrm|rlm|nbsp);/))) { 25029 s = s.replace(m[0], unescape1); 25030 } 25031 return s; 25032 } 25033 25034 function shouldAdd(current, element) { 25035 return !NEEDS_PARENT[element.localName] || 25036 NEEDS_PARENT[element.localName] === current.localName; 25037 } 25038 25039 // Create an element for this tag. 25040 function createElement(type, annotation) { 25041 var tagName = TAG_NAME[type]; 25042 if (!tagName) { 25043 return null; 25044 } 25045 var element = window.document.createElement(tagName); 25046 element.localName = tagName; 25047 var name = TAG_ANNOTATION[type]; 25048 if (name && annotation) { 25049 element[name] = annotation.trim(); 25050 } 25051 return element; 25052 } 25053 25054 var rootDiv = window.document.createElement("div"), 25055 current = rootDiv, 25056 t, 25057 tagStack = []; 25058 25059 while ((t = nextToken()) !== null) { 25060 if (t[0] === '<') { 25061 if (t[1] === "/") { 25062 // If the closing tag matches, move back up to the parent node. 25063 if (tagStack.length && 25064 tagStack[tagStack.length - 1] === t.substr(2).replace(">", "")) { 25065 tagStack.pop(); 25066 current = current.parentNode; 25067 } 25068 // Otherwise just ignore the end tag. 25069 continue; 25070 } 25071 var ts = parseTimeStamp(t.substr(1, t.length - 2)); 25072 var node; 25073 if (ts) { 25074 // Timestamps are lead nodes as well. 25075 node = window.document.createProcessingInstruction("timestamp", ts); 25076 current.appendChild(node); 25077 continue; 25078 } 25079 var m = t.match(/^<([^.\s/0-9>]+)(\.[^\s\\>]+)?([^>\\]+)?(\\?)>?$/); 25080 // If we can't parse the tag, skip to the next tag. 25081 if (!m) { 25082 continue; 25083 } 25084 // Try to construct an element, and ignore the tag if we couldn't. 25085 node = createElement(m[1], m[3]); 25086 if (!node) { 25087 continue; 25088 } 25089 // Determine if the tag should be added based on the context of where it 25090 // is placed in the cuetext. 25091 if (!shouldAdd(current, node)) { 25092 continue; 25093 } 25094 // Set the class list (as a list of classes, separated by space). 25095 if (m[2]) { 25096 node.className = m[2].substr(1).replace('.', ' '); 25097 } 25098 // Append the node to the current node, and enter the scope of the new 25099 // node. 25100 tagStack.push(m[1]); 25101 current.appendChild(node); 25102 current = node; 25103 continue; 25104 } 25105 25106 // Text nodes are leaf nodes. 25107 current.appendChild(window.document.createTextNode(unescape(t))); 25108 } 25109 25110 return rootDiv; 25111} 25112 25113// This is a list of all the Unicode characters that have a strong 25114// right-to-left category. What this means is that these characters are 25115// written right-to-left for sure. It was generated by pulling all the strong 25116// right-to-left characters out of the Unicode data table. That table can 25117// found at: http://www.unicode.org/Public/UNIDATA/UnicodeData.txt 25118var strongRTLRanges = [[0x5be, 0x5be], [0x5c0, 0x5c0], [0x5c3, 0x5c3], [0x5c6, 0x5c6], 25119 [0x5d0, 0x5ea], [0x5f0, 0x5f4], [0x608, 0x608], [0x60b, 0x60b], [0x60d, 0x60d], 25120 [0x61b, 0x61b], [0x61e, 0x64a], [0x66d, 0x66f], [0x671, 0x6d5], [0x6e5, 0x6e6], 25121 [0x6ee, 0x6ef], [0x6fa, 0x70d], [0x70f, 0x710], [0x712, 0x72f], [0x74d, 0x7a5], 25122 [0x7b1, 0x7b1], [0x7c0, 0x7ea], [0x7f4, 0x7f5], [0x7fa, 0x7fa], [0x800, 0x815], 25123 [0x81a, 0x81a], [0x824, 0x824], [0x828, 0x828], [0x830, 0x83e], [0x840, 0x858], 25124 [0x85e, 0x85e], [0x8a0, 0x8a0], [0x8a2, 0x8ac], [0x200f, 0x200f], 25125 [0xfb1d, 0xfb1d], [0xfb1f, 0xfb28], [0xfb2a, 0xfb36], [0xfb38, 0xfb3c], 25126 [0xfb3e, 0xfb3e], [0xfb40, 0xfb41], [0xfb43, 0xfb44], [0xfb46, 0xfbc1], 25127 [0xfbd3, 0xfd3d], [0xfd50, 0xfd8f], [0xfd92, 0xfdc7], [0xfdf0, 0xfdfc], 25128 [0xfe70, 0xfe74], [0xfe76, 0xfefc], [0x10800, 0x10805], [0x10808, 0x10808], 25129 [0x1080a, 0x10835], [0x10837, 0x10838], [0x1083c, 0x1083c], [0x1083f, 0x10855], 25130 [0x10857, 0x1085f], [0x10900, 0x1091b], [0x10920, 0x10939], [0x1093f, 0x1093f], 25131 [0x10980, 0x109b7], [0x109be, 0x109bf], [0x10a00, 0x10a00], [0x10a10, 0x10a13], 25132 [0x10a15, 0x10a17], [0x10a19, 0x10a33], [0x10a40, 0x10a47], [0x10a50, 0x10a58], 25133 [0x10a60, 0x10a7f], [0x10b00, 0x10b35], [0x10b40, 0x10b55], [0x10b58, 0x10b72], 25134 [0x10b78, 0x10b7f], [0x10c00, 0x10c48], [0x1ee00, 0x1ee03], [0x1ee05, 0x1ee1f], 25135 [0x1ee21, 0x1ee22], [0x1ee24, 0x1ee24], [0x1ee27, 0x1ee27], [0x1ee29, 0x1ee32], 25136 [0x1ee34, 0x1ee37], [0x1ee39, 0x1ee39], [0x1ee3b, 0x1ee3b], [0x1ee42, 0x1ee42], 25137 [0x1ee47, 0x1ee47], [0x1ee49, 0x1ee49], [0x1ee4b, 0x1ee4b], [0x1ee4d, 0x1ee4f], 25138 [0x1ee51, 0x1ee52], [0x1ee54, 0x1ee54], [0x1ee57, 0x1ee57], [0x1ee59, 0x1ee59], 25139 [0x1ee5b, 0x1ee5b], [0x1ee5d, 0x1ee5d], [0x1ee5f, 0x1ee5f], [0x1ee61, 0x1ee62], 25140 [0x1ee64, 0x1ee64], [0x1ee67, 0x1ee6a], [0x1ee6c, 0x1ee72], [0x1ee74, 0x1ee77], 25141 [0x1ee79, 0x1ee7c], [0x1ee7e, 0x1ee7e], [0x1ee80, 0x1ee89], [0x1ee8b, 0x1ee9b], 25142 [0x1eea1, 0x1eea3], [0x1eea5, 0x1eea9], [0x1eeab, 0x1eebb], [0x10fffd, 0x10fffd]]; 25143 25144function isStrongRTLChar(charCode) { 25145 for (var i = 0; i < strongRTLRanges.length; i++) { 25146 var currentRange = strongRTLRanges[i]; 25147 if (charCode >= currentRange[0] && charCode <= currentRange[1]) { 25148 return true; 25149 } 25150 } 25151 25152 return false; 25153} 25154 25155function determineBidi(cueDiv) { 25156 var nodeStack = [], 25157 text = "", 25158 charCode; 25159 25160 if (!cueDiv || !cueDiv.childNodes) { 25161 return "ltr"; 25162 } 25163 25164 function pushNodes(nodeStack, node) { 25165 for (var i = node.childNodes.length - 1; i >= 0; i--) { 25166 nodeStack.push(node.childNodes[i]); 25167 } 25168 } 25169 25170 function nextTextNode(nodeStack) { 25171 if (!nodeStack || !nodeStack.length) { 25172 return null; 25173 } 25174 25175 var node = nodeStack.pop(), 25176 text = node.textContent || node.innerText; 25177 if (text) { 25178 // TODO: This should match all unicode type B characters (paragraph 25179 // separator characters). See issue #115. 25180 var m = text.match(/^.*(\n|\r)/); 25181 if (m) { 25182 nodeStack.length = 0; 25183 return m[0]; 25184 } 25185 return text; 25186 } 25187 if (node.tagName === "ruby") { 25188 return nextTextNode(nodeStack); 25189 } 25190 if (node.childNodes) { 25191 pushNodes(nodeStack, node); 25192 return nextTextNode(nodeStack); 25193 } 25194 } 25195 25196 pushNodes(nodeStack, cueDiv); 25197 while ((text = nextTextNode(nodeStack))) { 25198 for (var i = 0; i < text.length; i++) { 25199 charCode = text.charCodeAt(i); 25200 if (isStrongRTLChar(charCode)) { 25201 return "rtl"; 25202 } 25203 } 25204 } 25205 return "ltr"; 25206} 25207 25208function computeLinePos(cue) { 25209 if (typeof cue.line === "number" && 25210 (cue.snapToLines || (cue.line >= 0 && cue.line <= 100))) { 25211 return cue.line; 25212 } 25213 if (!cue.track || !cue.track.textTrackList || 25214 !cue.track.textTrackList.mediaElement) { 25215 return -1; 25216 } 25217 var track = cue.track, 25218 trackList = track.textTrackList, 25219 count = 0; 25220 for (var i = 0; i < trackList.length && trackList[i] !== track; i++) { 25221 if (trackList[i].mode === "showing") { 25222 count++; 25223 } 25224 } 25225 return ++count * -1; 25226} 25227 25228function StyleBox() { 25229} 25230 25231// Apply styles to a div. If there is no div passed then it defaults to the 25232// div on 'this'. 25233StyleBox.prototype.applyStyles = function(styles, div) { 25234 div = div || this.div; 25235 for (var prop in styles) { 25236 if (styles.hasOwnProperty(prop)) { 25237 div.style[prop] = styles[prop]; 25238 } 25239 } 25240}; 25241 25242StyleBox.prototype.formatStyle = function(val, unit) { 25243 return val === 0 ? 0 : val + unit; 25244}; 25245 25246// Constructs the computed display state of the cue (a div). Places the div 25247// into the overlay which should be a block level element (usually a div). 25248function CueStyleBox(window, cue, styleOptions) { 25249 var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent); 25250 var color = "rgba(255, 255, 255, 1)"; 25251 var backgroundColor = "rgba(0, 0, 0, 0.8)"; 25252 25253 if (isIE8) { 25254 color = "rgb(255, 255, 255)"; 25255 backgroundColor = "rgb(0, 0, 0)"; 25256 } 25257
25258 StyleBox.call(this); 25259 this.cue = cue; 25260 25261 // Parse our cue's text into a DOM tree rooted at 'cueDiv'. This div will 25262 // have inline positioning and will function as the cue background box. 25263 this.cueDiv = parseContent(window, cue.text); 25264 var styles = { 25265 color: color, 25266 backgroundColor: backgroundColor, 25267 position: "relative", 25268 left: 0, 25269 right: 0, 25270 top: 0, 25271 bottom: 0, 25272 display: "inline" 25273 }; 25274 25275 if (!isIE8) { 25276 styles.writingMode = cue.vertical === "" ? "horizontal-tb" 25277 : cue.vertical === "lr" ? "vertical-lr" 25278 : "vertical-rl"; 25279 styles.unicodeBidi = "plaintext"; 25280 } 25281 this.applyStyles(styles, this.cueDiv); 25282 25283 // Create an absolutely positioned div that will be used to position the cue 25284 // div. Note, all WebVTT cue-setting alignments are equivalent to the CSS 25285 // mirrors of them except "middle" which is "center" in CSS. 25286 this.div = window.document.createElement("div"); 25287 styles = { 25288 textAlign: cue.align === "middle" ? "center" : cue.align, 25289 font: styleOptions.font, 25290 whiteSpace: "pre-line", 25291 position: "absolute" 25292 }; 25293 25294 if (!isIE8) { 25295 styles.direction = determineBidi(this.cueDiv); 25296 styles.writingMode = cue.vertical === "" ? "horizontal-tb" 25297 : cue.vertical === "lr" ? "vertical-lr" 25298 : "vertical-rl". 25299 stylesunicodeBidi = "plaintext"; 25300 } 25301 25302 this.applyStyles(styles); 25303 25304 this.div.appendChild(this.cueDiv); 25305 25306 // Calculate the distance from the reference edge of the viewport to the text 25307 // position of the cue box. The reference edge will be resolved later when 25308 // the box orientation styles are applied. 25309 var textPos = 0; 25310 switch (cue.positionAlign) { 25311 case "start": 25312 textPos = cue.position; 25313 break; 25314 case "middle": 25315 textPos = cue.position - (cue.size / 2); 25316 break; 25317 case "end": 25318 textPos = cue.position - cue.size; 25319 break; 25320 } 25321 25322 // Horizontal box orientation; textPos is the distance from the left edge of the 25323 // area to the left edge of the box and cue.size is the distance extending to 25324 // the right from there. 25325 if (cue.vertical === "") { 25326 this.applyStyles({ 25327 left: this.formatStyle(textPos, "%"), 25328 width: this.formatStyle(cue.size, "%") 25329 }); 25330 // Vertical box orientation; textPos is the distance from the top edge of the 25331 // area to the top edge of the box and cue.size is the height extending 25332 // downwards from there. 25333 } else { 25334 this.applyStyles({ 25335 top: this.formatStyle(textPos, "%"), 25336 height: this.formatStyle(cue.size, "%") 25337 }); 25338 } 25339 25340 this.move = function(box) { 25341 this.applyStyles({ 25342 top: this.formatStyle(box.top, "px"), 25343 bottom: this.formatStyle(box.bottom, "px"), 25344 left: this.formatStyle(box.left, "px"), 25345 right: this.formatStyle(box.right, "px"), 25346 height: this.formatStyle(box.height, "px"), 25347 width: this.formatStyle(box.width, "px") 25348 }); 25349 }; 25350} 25351CueStyleBox.prototype = _objCreate(StyleBox.prototype); 25352CueStyleBox.prototype.constructor = CueStyleBox; 25353 25354// Represents the co-ordinates of an Element in a way that we can easily 25355// compute things with such as if it overlaps or intersects with another Element. 25356// Can initialize it with either a StyleBox or another BoxPosition. 25357function BoxPosition(obj) { 25358 var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent); 25359 25360 // Either a BoxPosition was passed in and we need to copy it, or a StyleBox 25361 // was passed in and we need to copy the results of 'getBoundingClientRect' 25362 // as the object returned is readonly. All co-ordinate values are in reference 25363 // to the viewport origin (top left). 25364 var lh, height, width, top; 25365 if (obj.div) { 25366 height = obj.div.offsetHeight; 25367 width = obj.div.offsetWidth; 25368 top = obj.div.offsetTop; 25369 25370 var rects = (rects = obj.div.childNodes) && (rects = rects[0]) && 25371 rects.getClientRects && rects.getClientRects(); 25372 obj = obj.div.getBoundingClientRect(); 25373 // In certain cases the outter div will be slightly larger then the sum of 25374 // the inner div's lines. This could be due to bold text, etc, on some platforms. 25375 // In this case we should get the average line height and use that. This will 25376 // result in the desired behaviour. 25377 lh = rects ? Math.max((rects[0] && rects[0].height) || 0, obj.height / rects.length) 25378 : 0; 25379 25380 } 25381 this.left = obj.left; 25382 this.right = obj.right; 25383 this.top = obj.top || top; 25384 this.height = obj.height || height; 25385 this.bottom = obj.bottom || (top + (obj.height || height)); 25386 this.width = obj.width || width; 25387 this.lineHeight = lh !== undefined ? lh : obj.lineHeight; 25388 25389 if (isIE8 && !this.lineHeight) { 25390 this.lineHeight = 13; 25391 }
vendor: 20,253 bytes, lines 25391-26045
25391 25392} 25393 25394// Move the box along a particular axis. Optionally pass in an amount to move 25395// the box. If no amount is passed then the default is the line height of the 25396// box. 25397BoxPosition.prototype.move = function(axis, toMove) { 25398 toMove = toMove !== undefined ? toMove : this.lineHeight; 25399 switch (axis) { 25400 case "+x": 25401 this.left += toMove; 25402 this.right += toMove; 25403 break; 25404 case "-x": 25405 this.left -= toMove; 25406 this.right -= toMove; 25407 break; 25408 case "+y": 25409 this.top += toMove; 25410 this.bottom += toMove; 25411 break; 25412 case "-y": 25413 this.top -= toMove; 25414 this.bottom -= toMove; 25415 break; 25416 } 25417}; 25418 25419// Check if this box overlaps another box, b2. 25420BoxPosition.prototype.overlaps = function(b2) { 25421 return this.left < b2.right && 25422 this.right > b2.left && 25423 this.top < b2.bottom && 25424 this.bottom > b2.top; 25425}; 25426 25427// Check if this box overlaps any other boxes in boxes. 25428BoxPosition.prototype.overlapsAny = function(boxes) { 25429 for (var i = 0; i < boxes.length; i++) { 25430 if (this.overlaps(boxes[i])) { 25431 return true; 25432 } 25433 } 25434 return false; 25435}; 25436 25437// Check if this box is within another box. 25438BoxPosition.prototype.within = function(container) { 25439 return this.top >= container.top && 25440 this.bottom <= container.bottom && 25441 this.left >= container.left && 25442 this.right <= container.right; 25443}; 25444 25445// Check if this box is entirely within the container or it is overlapping 25446// on the edge opposite of the axis direction passed. For example, if "+x" is 25447// passed and the box is overlapping on the left edge of the container, then 25448// return true. 25449BoxPosition.prototype.overlapsOppositeAxis = function(container, axis) { 25450 switch (axis) { 25451 case "+x": 25452 return this.left < container.left; 25453 case "-x": 25454 return this.right > container.right; 25455 case "+y": 25456 return this.top < container.top; 25457 case "-y": 25458 return this.bottom > container.bottom; 25459 } 25460}; 25461 25462// Find the percentage of the area that this box is overlapping with another 25463// box. 25464BoxPosition.prototype.intersectPercentage = function(b2) { 25465 var x = Math.max(0, Math.min(this.right, b2.right) - Math.max(this.left, b2.left)), 25466 y = Math.max(0, Math.min(this.bottom, b2.bottom) - Math.max(this.top, b2.top)), 25467 intersectArea = x * y; 25468 return intersectArea / (this.height * this.width); 25469}; 25470 25471// Convert the positions from this box to CSS compatible positions using 25472// the reference container's positions. This has to be done because this 25473// box's positions are in reference to the viewport origin, whereas, CSS 25474// values are in referecne to their respective edges. 25475BoxPosition.prototype.toCSSCompatValues = function(reference) { 25476 return { 25477 top: this.top - reference.top, 25478 bottom: reference.bottom - this.bottom, 25479 left: this.left - reference.left, 25480 right: reference.right - this.right, 25481 height: this.height, 25482 width: this.width 25483 }; 25484}; 25485 25486// Get an object that represents the box's position without anything extra. 25487// Can pass a StyleBox, HTMLElement, or another BoxPositon. 25488BoxPosition.getSimpleBoxPosition = function(obj) { 25489 var height = obj.div ? obj.div.offsetHeight : obj.tagName ? obj.offsetHeight : 0; 25490 var width = obj.div ? obj.div.offsetWidth : obj.tagName ? obj.offsetWidth : 0; 25491 var top = obj.div ? obj.div.offsetTop : obj.tagName ? obj.offsetTop : 0; 25492 25493 obj = obj.div ? obj.div.getBoundingClientRect() : 25494 obj.tagName ? obj.getBoundingClientRect() : obj; 25495 var ret = { 25496 left: obj.left, 25497 right: obj.right, 25498 top: obj.top || top, 25499 height: obj.height || height, 25500 bottom: obj.bottom || (top + (obj.height || height)), 25501 width: obj.width || width 25502 }; 25503 return ret; 25504}; 25505 25506// Move a StyleBox to its specified, or next best, position. The containerBox 25507// is the box that contains the StyleBox, such as a div. boxPositions are 25508// a list of other boxes that the styleBox can't overlap with. 25509function moveBoxToLinePosition(window, styleBox, containerBox, boxPositions) { 25510 25511 // Find the best position for a cue box, b, on the video. The axis parameter 25512 // is a list of axis, the order of which, it will move the box along. For example: 25513 // Passing ["+x", "-x"] will move the box first along the x axis in the positive 25514 // direction. If it doesn't find a good position for it there it will then move 25515 // it along the x axis in the negative direction. 25516 function findBestPosition(b, axis) { 25517 var bestPosition, 25518 specifiedPosition = new BoxPosition(b), 25519 percentage = 1; // Highest possible so the first thing we get is better. 25520 25521 for (var i = 0; i < axis.length; i++) { 25522 while (b.overlapsOppositeAxis(containerBox, axis[i]) || 25523 (b.within(containerBox) && b.overlapsAny(boxPositions))) { 25524 b.move(axis[i]); 25525 } 25526 // We found a spot where we aren't overlapping anything. This is our 25527 // best position. 25528 if (b.within(containerBox)) { 25529 return b; 25530 } 25531 var p = b.intersectPercentage(containerBox); 25532 // If we're outside the container box less then we were on our last try 25533 // then remember this position as the best position. 25534 if (percentage > p) { 25535 bestPosition = new BoxPosition(b); 25536 percentage = p; 25537 } 25538 // Reset the box position to the specified position. 25539 b = new BoxPosition(specifiedPosition); 25540 } 25541 return bestPosition || specifiedPosition; 25542 } 25543 25544 var boxPosition = new BoxPosition(styleBox), 25545 cue = styleBox.cue, 25546 linePos = computeLinePos(cue), 25547 axis = []; 25548 25549 // If we have a line number to align the cue to. 25550 if (cue.snapToLines) { 25551 var size; 25552 switch (cue.vertical) { 25553 case "": 25554 axis = [ "+y", "-y" ]; 25555 size = "height"; 25556 break; 25557 case "rl": 25558 axis = [ "+x", "-x" ]; 25559 size = "width"; 25560 break; 25561 case "lr": 25562 axis = [ "-x", "+x" ]; 25563 size = "width"; 25564 break; 25565 } 25566 25567 var step = boxPosition.lineHeight, 25568 position = step * Math.round(linePos), 25569 maxPosition = containerBox[size] + step, 25570 initialAxis = axis[0]; 25571 25572 // If the specified intial position is greater then the max position then 25573 // clamp the box to the amount of steps it would take for the box to 25574 // reach the max position. 25575 if (Math.abs(position) > maxPosition) { 25576 position = position < 0 ? -1 : 1; 25577 position *= Math.ceil(maxPosition / step) * step; 25578 } 25579 25580 // If computed line position returns negative then line numbers are 25581 // relative to the bottom of the video instead of the top. Therefore, we 25582 // need to increase our initial position by the length or width of the 25583 // video, depending on the writing direction, and reverse our axis directions. 25584 if (linePos < 0) { 25585 position += cue.vertical === "" ? containerBox.height : containerBox.width; 25586 axis = axis.reverse(); 25587 } 25588 25589 // Move the box to the specified position. This may not be its best 25590 // position. 25591 boxPosition.move(initialAxis, position); 25592 25593 } else { 25594 // If we have a percentage line value for the cue. 25595 var calculatedPercentage = (boxPosition.lineHeight / containerBox.height) * 100; 25596 25597 switch (cue.lineAlign) { 25598 case "middle": 25599 linePos -= (calculatedPercentage / 2); 25600 break; 25601 case "end": 25602 linePos -= calculatedPercentage; 25603 break; 25604 } 25605 25606 // Apply initial line position to the cue box. 25607 switch (cue.vertical) { 25608 case "": 25609 styleBox.applyStyles({ 25610 top: styleBox.formatStyle(linePos, "%") 25611 }); 25612 break; 25613 case "rl": 25614 styleBox.applyStyles({ 25615 left: styleBox.formatStyle(linePos, "%") 25616 }); 25617 break; 25618 case "lr": 25619 styleBox.applyStyles({ 25620 right: styleBox.formatStyle(linePos, "%") 25621 }); 25622 break; 25623 } 25624 25625 axis = [ "+y", "-x", "+x", "-y" ]; 25626 25627 // Get the box position again after we've applied the specified positioning 25628 // to it. 25629 boxPosition = new BoxPosition(styleBox); 25630 } 25631 25632 var bestPosition = findBestPosition(boxPosition, axis); 25633 styleBox.move(bestPosition.toCSSCompatValues(containerBox)); 25634} 25635 25636function WebVTT() { 25637 // Nothing 25638} 25639 25640// Helper to allow strings to be decoded instead of the default binary utf8 data. 25641WebVTT.StringDecoder = function() { 25642 return { 25643 decode: function(data) { 25644 if (!data) { 25645 return ""; 25646 } 25647 if (typeof data !== "string") { 25648 throw new Error("Error - expected string data."); 25649 } 25650 return decodeURIComponent(encodeURIComponent(data)); 25651 } 25652 }; 25653}; 25654 25655WebVTT.convertCueToDOMTree = function(window, cuetext) { 25656 if (!window || !cuetext) { 25657 return null; 25658 } 25659 return parseContent(window, cuetext); 25660}; 25661 25662var FONT_SIZE_PERCENT = 0.05; 25663var FONT_STYLE = "sans-serif"; 25664var CUE_BACKGROUND_PADDING = "1.5%"; 25665 25666// Runs the processing model over the cues and regions passed to it. 25667// @param overlay A block level element (usually a div) that the computed cues 25668// and regions will be placed into. 25669WebVTT.processCues = function(window, cues, overlay) { 25670 if (!window || !cues || !overlay) { 25671 return null; 25672 } 25673 25674 // Remove all previous children. 25675 while (overlay.firstChild) { 25676 overlay.removeChild(overlay.firstChild); 25677 } 25678 25679 var paddedOverlay = window.document.createElement("div"); 25680 paddedOverlay.style.position = "absolute"; 25681 paddedOverlay.style.left = "0"; 25682 paddedOverlay.style.right = "0"; 25683 paddedOverlay.style.top = "0"; 25684 paddedOverlay.style.bottom = "0"; 25685 paddedOverlay.style.margin = CUE_BACKGROUND_PADDING; 25686 overlay.appendChild(paddedOverlay); 25687 25688 // Determine if we need to compute the display states of the cues. This could 25689 // be the case if a cue's state has been changed since the last computation or 25690 // if it has not been computed yet. 25691 function shouldCompute(cues) { 25692 for (var i = 0; i < cues.length; i++) { 25693 if (cues[i].hasBeenReset || !cues[i].displayState) { 25694 return true; 25695 } 25696 } 25697 return false; 25698 } 25699 25700 // We don't need to recompute the cues' display states. Just reuse them. 25701 if (!shouldCompute(cues)) { 25702 for (var i = 0; i < cues.length; i++) { 25703 paddedOverlay.appendChild(cues[i].displayState); 25704 } 25705 return; 25706 } 25707 25708 var boxPositions = [], 25709 containerBox = BoxPosition.getSimpleBoxPosition(paddedOverlay), 25710 fontSize = Math.round(containerBox.height * FONT_SIZE_PERCENT * 100) / 100; 25711 var styleOptions = { 25712 font: fontSize + "px " + FONT_STYLE 25713 }; 25714 25715 (function() { 25716 var styleBox, cue; 25717 25718 for (var i = 0; i < cues.length; i++) { 25719 cue = cues[i]; 25720 25721 // Compute the intial position and styles of the cue div. 25722 styleBox = new CueStyleBox(window, cue, styleOptions); 25723 paddedOverlay.appendChild(styleBox.div); 25724 25725 // Move the cue div to it's correct line position. 25726 moveBoxToLinePosition(window, styleBox, containerBox, boxPositions); 25727 25728 // Remember the computed div so that we don't have to recompute it later 25729 // if we don't have too. 25730 cue.displayState = styleBox.div; 25731 25732 boxPositions.push(BoxPosition.getSimpleBoxPosition(styleBox)); 25733 } 25734 })(); 25735}; 25736 25737WebVTT.Parser = function(window, vttjs, decoder) { 25738 if (!decoder) { 25739 decoder = vttjs; 25740 vttjs = {}; 25741 } 25742 if (!vttjs) { 25743 vttjs = {}; 25744 } 25745 25746 this.window = window; 25747 this.vttjs = vttjs; 25748 this.state = "INITIAL"; 25749 this.buffer = ""; 25750 this.decoder = decoder || new TextDecoder("utf8"); 25751 this.regionList = []; 25752}; 25753 25754WebVTT.Parser.prototype = { 25755 // If the error is a ParsingError then report it to the consumer if 25756 // possible. If it's not a ParsingError then throw it like normal. 25757 reportOrThrowError: function(e) { 25758 if (e instanceof ParsingError) { 25759 this.onparsingerror && this.onparsingerror(e); 25760 } else { 25761 throw e; 25762 } 25763 }, 25764 parse: function (data) { 25765 var self = this; 25766 25767 // If there is no data then we won't decode it, but will just try to parse 25768 // whatever is in buffer already. This may occur in circumstances, for 25769 // example when flush() is called. 25770 if (data) { 25771 // Try to decode the data that we received. 25772 self.buffer += self.decoder.decode(data, {stream: true}); 25773 } 25774 25775 function collectNextLine() { 25776 var buffer = self.buffer; 25777 var pos = 0; 25778 while (pos < buffer.length && buffer[pos] !== '\r' && buffer[pos] !== '\n') { 25779 ++pos; 25780 } 25781 var line = buffer.substr(0, pos); 25782 // Advance the buffer early in case we fail below. 25783 if (buffer[pos] === '\r') { 25784 ++pos; 25785 } 25786 if (buffer[pos] === '\n') { 25787 ++pos; 25788 } 25789 self.buffer = buffer.substr(pos); 25790 return line; 25791 } 25792 25793 // 3.4 WebVTT region and WebVTT region settings syntax 25794 function parseRegion(input) { 25795 var settings = new Settings(); 25796 25797 parseOptions(input, function (k, v) { 25798 switch (k) { 25799 case "id": 25800 settings.set(k, v); 25801 break; 25802 case "width": 25803 settings.percent(k, v); 25804 break; 25805 case "lines": 25806 settings.integer(k, v); 25807 break; 25808 case "regionanchor": 25809 case "viewportanchor": 25810 var xy = v.split(','); 25811 if (xy.length !== 2) { 25812 break; 25813 } 25814 // We have to make sure both x and y parse, so use a temporary 25815 // settings object here. 25816 var anchor = new Settings(); 25817 anchor.percent("x", xy[0]); 25818 anchor.percent("y", xy[1]); 25819 if (!anchor.has("x") || !anchor.has("y")) { 25820 break; 25821 } 25822 settings.set(k + "X", anchor.get("x")); 25823 settings.set(k + "Y", anchor.get("y")); 25824 break; 25825 case "scroll": 25826 settings.alt(k, v, ["up"]); 25827 break; 25828 } 25829 }, /=/, /\s/); 25830 25831 // Create the region, using default values for any values that were not 25832 // specified. 25833 if (settings.has("id")) { 25834 var region = new (self.vttjs.VTTRegion || self.window.VTTRegion)(); 25835 region.width = settings.get("width", 100); 25836 region.lines = settings.get("lines", 3); 25837 region.regionAnchorX = settings.get("regionanchorX", 0); 25838 region.regionAnchorY = settings.get("regionanchorY", 100); 25839 region.viewportAnchorX = settings.get("viewportanchorX", 0); 25840 region.viewportAnchorY = settings.get("viewportanchorY", 100); 25841 region.scroll = settings.get("scroll", ""); 25842 // Register the region. 25843 self.onregion && self.onregion(region); 25844 // Remember the VTTRegion for later in case we parse any VTTCues that 25845 // reference it. 25846 self.regionList.push({ 25847 id: settings.get("id"), 25848 region: region 25849 }); 25850 } 25851 } 25852 25853 // draft-pantos-http-live-streaming-20 25854 // https://tools.ietf.org/html/draft-pantos-http-live-streaming-20#section-3.5 25855 // 3.5 WebVTT 25856 function parseTimestampMap(input) { 25857 var settings = new Settings(); 25858 25859 parseOptions(input, function(k, v) { 25860 switch(k) { 25861 case "MPEGT": 25862 settings.integer(k + 'S', v); 25863 break; 25864 case "LOCA": 25865 settings.set(k + 'L', parseTimeStamp(v)); 25866 break; 25867 } 25868 }, /[^\d]:/, /,/); 25869 25870 self.ontimestampmap && self.ontimestampmap({ 25871 "MPEGTS": settings.get("MPEGTS"), 25872 "LOCAL": settings.get("LOCAL") 25873 }); 25874 } 25875 25876 // 3.2 WebVTT metadata header syntax 25877 function parseHeader(input) { 25878 if (input.match(/X-TIMESTAMP-MAP/)) { 25879 // This line contains HLS X-TIMESTAMP-MAP metadata 25880 parseOptions(input, function(k, v) { 25881 switch(k) { 25882 case "X-TIMESTAMP-MAP": 25883 parseTimestampMap(v); 25884 break; 25885 } 25886 }, /=/); 25887 } else { 25888 parseOptions(input, function (k, v) { 25889 switch (k) { 25890 case "Region": 25891 // 3.3 WebVTT region metadata header syntax 25892 parseRegion(v); 25893 break; 25894 } 25895 }, /:/); 25896 } 25897 25898 } 25899 25900 // 5.1 WebVTT file parsing. 25901 try { 25902 var line; 25903 if (self.state === "INITIAL") { 25904 // We can't start parsing until we have the first line. 25905 if (!/\r\n|\n/.test(self.buffer)) { 25906 return this; 25907 } 25908 25909 line = collectNextLine(); 25910 25911 var m = line.match(/^WEBVTT([ \t].*)?$/); 25912 if (!m || !m[0]) { 25913 throw new ParsingError(ParsingError.Errors.BadSignature); 25914 } 25915 25916 self.state = "HEADER"; 25917 } 25918 25919 var alreadyCollectedLine = false; 25920 while (self.buffer) { 25921 // We can't parse a line until we have the full line. 25922 if (!/\r\n|\n/.test(self.buffer)) { 25923 return this; 25924 } 25925 25926 if (!alreadyCollectedLine) { 25927 line = collectNextLine(); 25928 } else { 25929 alreadyCollectedLine = false; 25930 } 25931 25932 switch (self.state) { 25933 case "HEADER": 25934 // 13-18 - Allow a header (metadata) under the WEBVTT line. 25935 if (/:/.test(line)) { 25936 parseHeader(line); 25937 } else if (!line) { 25938 // An empty line terminates the header and starts the body (cues). 25939 self.state = "ID"; 25940 } 25941 continue; 25942 case "NOTE": 25943 // Ignore NOTE blocks. 25944 if (!line) { 25945 self.state = "ID"; 25946 } 25947 continue; 25948 case "ID": 25949 // Check for the start of NOTE blocks. 25950 if (/^NOTE($|[ \t])/.test(line)) { 25951 self.state = "NOTE"; 25952 break; 25953 } 25954 // 19-29 - Allow any number of line terminators, then initialize new cue values. 25955 if (!line) { 25956 continue; 25957 } 25958 self.cue = new (self.vttjs.VTTCue || self.window.VTTCue)(0, 0, ""); 25959 self.state = "CUE"; 25960 // 30-39 - Check if self line contains an optional identifier or timing data. 25961 if (line.indexOf("-->") === -1) { 25962 self.cue.id = line; 25963 continue; 25964 } 25965 // Process line as start of a cue. 25966 /*falls through*/ 25967 case "CUE": 25968 // 40 - Collect cue timings and settings. 25969 try { 25970 parseCue(line, self.cue, self.regionList); 25971 } catch (e) { 25972 self.reportOrThrowError(e); 25973 // In case of an error ignore rest of the cue. 25974 self.cue = null; 25975 self.state = "BADCUE"; 25976 continue; 25977 } 25978 self.state = "CUETEXT"; 25979 continue; 25980 case "CUETEXT": 25981 var hasSubstring = line.indexOf("-->") !== -1; 25982 // 34 - If we have an empty line then report the cue. 25983 // 35 - If we have the special substring '-->' then report the cue, 25984 // but do not collect the line as we need to process the current 25985 // one as a new cue. 25986 if (!line || hasSubstring && (alreadyCollectedLine = true)) { 25987 // We are done parsing self cue. 25988 self.oncue && self.oncue(self.cue); 25989 self.cue = null; 25990 self.state = "ID"; 25991 continue; 25992 } 25993 if (self.cue.text) { 25994 self.cue.text += "\n"; 25995 } 25996 self.cue.text += line; 25997 continue; 25998 case "BADCUE": // BADCUE 25999 // 54-62 - Collect and discard the remaining cue. 26000 if (!line) { 26001 self.state = "ID"; 26002 } 26003 continue; 26004 } 26005 } 26006 } catch (e) { 26007 self.reportOrThrowError(e); 26008 26009 // If we are currently parsing a cue, report what we have. 26010 if (self.state === "CUETEXT" && self.cue && self.oncue) { 26011 self.oncue(self.cue); 26012 } 26013 self.cue = null; 26014 // Enter BADWEBVTT state if header was not parsed correctly otherwise 26015 // another exception occurred so enter BADCUE state. 26016 self.state = self.state === "INITIAL" ? "BADWEBVTT" : "BADCUE"; 26017 } 26018 return this; 26019 }, 26020 flush: function () { 26021 var self = this; 26022 try { 26023 // Finish decoding the stream. 26024 self.buffer += self.decoder.decode(); 26025 // Synthesize the end of the current cue or region. 26026 if (self.cue || self.state === "HEADER") { 26027 self.buffer += "\n\n"; 26028 self.parse(); 26029 } 26030 // If we've flushed, parsed, and we're still on the INITIAL state then 26031 // that means we don't have enough of the stream to parse the first 26032 // line. 26033 if (self.state === "INITIAL") { 26034 throw new ParsingError(ParsingError.Errors.BadSignature); 26035 } 26036 } catch(e) { 26037 self.reportOrThrowError(e); 26038 } 26039 self.onflush && self.onflush(); 26040 return this; 26041 } 26042}; 26043 26044module.exports = WebVTT; 26045
26046},{}],101:[function(_dereq_,module,exports){ 26047/** 26048 * Copyright 2013 vtt.js Contributors 26049 * 26050 * Licensed under the Apache License, Version 2.0 (the "License"); 26051 * you may not use this file except in compliance with the License. 26052 * You may obtain a copy of the License at 26053 * 26054 * http://www.apache.org/licenses/LICENSE-2.0 26055 * 26056 * Unless required by applicable law or agreed to in writing, software 26057 * distributed under the License is distributed on an "AS IS" BASIS, 26058 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 26059 * See the License for the specific language governing permissions and 26060 * limitations under the License. 26061 */ 26062 26063var autoKeyword = "auto"; 26064var directionSetting = { 26065 "": true, 26066 "lr": true, 26067 "rl": true 26068}; 26069var alignSetting = { 26070 "start": true, 26071 "middle": true, 26072 "end": true, 26073 "left": true, 26074 "right": true 26075}; 26076 26077function findDirectionSetting(value) { 26078 if (typeof value !== "string") { 26079 return false; 26080 } 26081 var dir = directionSetting[value.toLowerCase()]; 26082 return dir ? value.toLowerCase() : false; 26083} 26084 26085function findAlignSetting(value) { 26086 if (typeof value !== "string") { 26087 return false; 26088 } 26089 var align = alignSetting[value.toLowerCase()]; 26090 return align ? value.toLowerCase() : false; 26091} 26092 26093function extend(obj) { 26094 var i = 1; 26095 for (; i < arguments.length; i++) { 26096 var cobj = arguments[i]; 26097 for (var p in cobj) { 26098 obj[p] = cobj[p]; 26099 } 26100 } 26101 26102 return obj; 26103} 26104 26105function VTTCue(startTime, endTime, text) { 26106 var cue = this; 26107 var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent); 26108 var baseObj = {}; 26109 26110 if (isIE8) { 26111 cue = document.createElement('custom'); 26112 } else { 26113 baseObj.enumerable = true; 26114 } 26115 26116 /** 26117 * Shim implementation specific properties. These properties are not in 26118 * the spec. 26119 */ 26120 26121 // Lets us know when the VTTCue's data has changed in such a way that we need 26122 // to recompute its display state. This lets us compute its display state 26123 // lazily. 26124 cue.hasBeenReset = false; 26125 26126 /** 26127 * VTTCue and TextTrackCue properties 26128 * http://dev.w3.org/html5/webvtt/#vttcue-interface 26129 */ 26130 26131 var _id = ""; 26132 var _pauseOnExit = false; 26133 var _startTime = startTime; 26134 var _endTime = endTime; 26135 var _text = text; 26136 var _region = null; 26137 var _vertical = ""; 26138 var _snapToLines = true; 26139 var _line = "auto"; 26140 var _lineAlign = "start"; 26141 var _position = 50; 26142 var _positionAlign = "middle"; 26143 var _size = 50; 26144 var _align = "middle"; 26145 26146 Object.defineProperty(cue, 26147 "id", extend({}, baseObj, { 26148 get: function() { 26149 return _id; 26150 }, 26151 set: function(value) { 26152 _id = "" + value; 26153 } 26154 })); 26155 26156 Object.defineProperty(cue, 26157 "pauseOnExit", extend({}, baseObj, { 26158 get: function() { 26159 return _pauseOnExit; 26160 }, 26161 set: function(value) { 26162 _pauseOnExit = !!value; 26163 } 26164 })); 26165 26166 Object.defineProperty(cue, 26167 "startTime", extend({}, baseObj, { 26168 get: function() { 26169 return _startTime; 26170 }, 26171 set: function(value) { 26172 if (typeof value !== "number") { 26173 throw new TypeError("Start time must be set to a number."); 26174 } 26175 _startTime = value; 26176 this.hasBeenReset = true; 26177 } 26178 })); 26179 26180 Object.defineProperty(cue, 26181 "endTime", extend({}, baseObj, { 26182 get: function() { 26183 return _endTime; 26184 }, 26185 set: function(value) { 26186 if (typeof value !== "number") { 26187 throw new TypeError("End time must be set to a number."); 26188 } 26189 _endTime = value; 26190 this.hasBeenReset = true; 26191 } 26192 })); 26193 26194 Object.defineProperty(cue, 26195 "text", extend({}, baseObj, { 26196 get: function() { 26197 return _text; 26198 }, 26199 set: function(value) { 26200 _text = "" + value; 26201 this.hasBeenReset = true; 26202 } 26203 })); 26204 26205 Object.defineProperty(cue, 26206 "region", extend({}, baseObj, { 26207 get: function() { 26208 return _region; 26209 }, 26210 set: function(value) { 26211 _region = value; 26212 this.hasBeenReset = true; 26213 } 26214 })); 26215 26216 Object.defineProperty(cue, 26217 "vertical", extend({}, baseObj, { 26218 get: function() { 26219 return _vertical; 26220 }, 26221 set: function(value) { 26222 var setting = findDirectionSetting(value); 26223 // Have to check for false because the setting an be an empty string. 26224 if (setting === false) { 26225 throw new SyntaxError("An invalid or illegal string was specified."); 26226 } 26227 _vertical = setting; 26228 this.hasBeenReset = true; 26229 } 26230 })); 26231 26232 Object.defineProperty(cue, 26233 "snapToLines", extend({}, baseObj, { 26234 get: function() { 26235 return _snapToLines; 26236 }, 26237 set: function(value) { 26238 _snapToLines = !!value; 26239 this.hasBeenReset = true; 26240 } 26241 })); 26242 26243 Object.defineProperty(cue, 26244 "line", extend({}, baseObj, { 26245 get: function() { 26246 return _line; 26247 }, 26248 set: function(value) {
26249 if (typeof value !== "number" && value !== autoKeyword) { 26250 throw new SyntaxError("An invalid number or illegal string was specified."); 26251 } 26252 _line = value; 26253 this.hasBeenReset = true; 26254 } 26255 })); 26256 26257 Object.defineProperty(cue, 26258 "lineAlign", extend({}, baseObj, { 26259 get: function() { 26260 return _lineAlign; 26261 }, 26262 set: function(value) { 26263 var setting = findAlignSetting(value); 26264 if (!setting) { 26265 throw new SyntaxError("An invalid or illegal string was specified."); 26266 } 26267 _lineAlign = setting; 26268 this.hasBeenReset = true; 26269 } 26270 })); 26271 26272 Object.defineProperty(cue, 26273 "position", extend({}, baseObj, { 26274 get: function() { 26275 return _position; 26276 }, 26277 set: function(value) { 26278 if (value < 0 || value > 100) { 26279 throw new Error("Position must be between 0 and 100."); 26280 } 26281 _position = value; 26282 this.hasBeenReset = true; 26283 } 26284 })); 26285 26286 Object.defineProperty(cue, 26287 "positionAlign", extend({}, baseObj, { 26288 get: function() { 26289 return _positionAlign; 26290 }, 26291 set: function(value) { 26292 var setting = findAlignSetting(value); 26293 if (!setting) { 26294 throw new SyntaxError("An invalid or illegal string was specified."); 26295 } 26296 _positionAlign = setting; 26297 this.hasBeenReset = true; 26298 } 26299 })); 26300 26301 Object.defineProperty(cue, 26302 "size", extend({}, baseObj, { 26303 get: function() { 26304 return _size; 26305 }, 26306 set: function(value) { 26307 if (value < 0 || value > 100) { 26308 throw new Error("Size must be between 0 and 100."); 26309 } 26310 _size = value; 26311 this.hasBeenReset = true; 26312 } 26313 })); 26314 26315 Object.defineProperty(cue, 26316 "align", extend({}, baseObj, { 26317 get: function() { 26318 return _align; 26319 }, 26320 set: function(value) { 26321 var setting = findAlignSetting(value); 26322 if (!setting) { 26323 throw new SyntaxError("An invalid or illegal string was specified."); 26324 } 26325 _align = setting; 26326 this.hasBeenReset = true; 26327 } 26328 })); 26329 26330 /** 26331 * Other <track> spec defined properties 26332 */ 26333 26334 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#text-track-cue-display-state 26335 cue.displayState = undefined; 26336 26337 if (isIE8) { 26338 return cue; 26339 } 26340} 26341 26342/** 26343 * VTTCue methods 26344 */ 26345 26346VTTCue.prototype.getCueAsHTML = function() { 26347 // Assume WebVTT.convertCueToDOMTree is on the global. 26348 return WebVTT.convertCueToDOMTree(window, this.text); 26349}; 26350 26351module.exports = VTTCue; 26352 26353},{}],102:[function(_dereq_,module,exports){ 26354/** 26355 * Copyright 2013 vtt.js Contributors 26356 * 26357 * Licensed under the Apache License, Version 2.0 (the "License"); 26358 * you may not use this file except in compliance with the License. 26359 * You may obtain a copy of the License at 26360 * 26361 * http://www.apache.org/licenses/LICENSE-2.0 26362 * 26363 * Unless required by applicable law or agreed to in writing, software 26364 * distributed under the License is distributed on an "AS IS" BASIS, 26365 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 26366 * See the License for the specific language governing permissions and 26367 * limitations under the License. 26368 */ 26369 26370var scrollSetting = { 26371 "": true, 26372 "up": true 26373}; 26374 26375function findScrollSetting(value) { 26376 if (typeof value !== "string") { 26377 return false; 26378 } 26379 var scroll = scrollSetting[value.toLowerCase()]; 26380 return scroll ? value.toLowerCase() : false; 26381} 26382 26383function isValidPercentValue(value) { 26384 return typeof value === "number" && (value >= 0 && value <= 100); 26385} 26386 26387// VTTRegion shim http://dev.w3.org/html5/webvtt/#vttregion-interface 26388function VTTRegion() { 26389 var _width = 100; 26390 var _lines = 3; 26391 var _regionAnchorX = 0; 26392 var _regionAnchorY = 100; 26393 var _viewportAnchorX = 0; 26394 var _viewportAnchorY = 100; 26395 var _scroll = ""; 26396 26397 Object.defineProperties(this, { 26398 "width": { 26399 enumerable: true, 26400 get: function() { 26401 return _width; 26402 }, 26403 set: function(value) { 26404 if (!isValidPercentValue(value)) { 26405 throw new Error("Width must be between 0 and 100."); 26406 } 26407 _width = value; 26408 } 26409 }, 26410 "lines": { 26411 enumerable: true, 26412 get: function() { 26413 return _lines; 26414 }, 26415 set: function(value) {
vendor: 9,167 bytes, lines 26416-26743
26416 if (typeof value !== "number") { 26417 throw new TypeError("Lines must be set to a number."); 26418 } 26419 _lines = value; 26420 } 26421 }, 26422 "regionAnchorY": { 26423 enumerable: true, 26424 get: function() { 26425 return _regionAnchorY; 26426 }, 26427 set: function(value) { 26428 if (!isValidPercentValue(value)) { 26429 throw new Error("RegionAnchorX must be between 0 and 100."); 26430 } 26431 _regionAnchorY = value; 26432 } 26433 }, 26434 "regionAnchorX": { 26435 enumerable: true, 26436 get: function() { 26437 return _regionAnchorX; 26438 }, 26439 set: function(value) { 26440 if(!isValidPercentValue(value)) { 26441 throw new Error("RegionAnchorY must be between 0 and 100."); 26442 } 26443 _regionAnchorX = value; 26444 } 26445 }, 26446 "viewportAnchorY": { 26447 enumerable: true, 26448 get: function() { 26449 return _viewportAnchorY; 26450 }, 26451 set: function(value) { 26452 if (!isValidPercentValue(value)) { 26453 throw new Error("ViewportAnchorY must be between 0 and 100."); 26454 } 26455 _viewportAnchorY = value; 26456 } 26457 }, 26458 "viewportAnchorX": { 26459 enumerable: true, 26460 get: function() { 26461 return _viewportAnchorX; 26462 }, 26463 set: function(value) { 26464 if (!isValidPercentValue(value)) { 26465 throw new Error("ViewportAnchorX must be between 0 and 100."); 26466 } 26467 _viewportAnchorX = value; 26468 } 26469 }, 26470 "scroll": { 26471 enumerable: true, 26472 get: function() { 26473 return _scroll; 26474 }, 26475 set: function(value) { 26476 var setting = findScrollSetting(value); 26477 // Have to check for false as an empty string is a legal value. 26478 if (setting === false) { 26479 throw new SyntaxError("An invalid or illegal string was specified."); 26480 } 26481 _scroll = setting; 26482 } 26483 } 26484 }); 26485} 26486 26487module.exports = VTTRegion; 26488 26489},{}],103:[function(_dereq_,module,exports){ 26490(function (global){ 26491var win; 26492 26493if (typeof window !== "undefined") { 26494 win = window; 26495} else if (typeof global !== "undefined") { 26496 win = global; 26497} else if (typeof self !== "undefined"){ 26498 win = self; 26499} else { 26500 win = {}; 26501} 26502 26503module.exports = win; 26504 26505}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 26506},{}],104:[function(_dereq_,module,exports){ 26507"use strict"; 26508var window = _dereq_(95) 26509var isFunction = _dereq_(105) 26510var parseHeaders = _dereq_(108) 26511var xtend = _dereq_(109) 26512 26513module.exports = createXHR 26514createXHR.XMLHttpRequest = window.XMLHttpRequest || noop 26515createXHR.XDomainRequest = "withCredentials" in (new createXHR.XMLHttpRequest()) ? createXHR.XMLHttpRequest : window.XDomainRequest 26516 26517forEachArray(["get", "put", "post", "patch", "head", "delete"], function(method) { 26518 createXHR[method === "delete" ? "del" : method] = function(uri, options, callback) { 26519 options = initParams(uri, options, callback) 26520 options.method = method.toUpperCase() 26521 return _createXHR(options) 26522 } 26523}) 26524 26525function forEachArray(array, iterator) { 26526 for (var i = 0; i < array.length; i++) { 26527 iterator(array[i]) 26528 } 26529} 26530 26531function isEmpty(obj){ 26532 for(var i in obj){ 26533 if(obj.hasOwnProperty(i)) return false 26534 } 26535 return true 26536} 26537 26538function initParams(uri, options, callback) { 26539 var params = uri 26540 26541 if (isFunction(options)) { 26542 callback = options 26543 if (typeof uri === "string") { 26544 params = {uri:uri} 26545 } 26546 } else { 26547 params = xtend(options, {uri: uri}) 26548 } 26549 26550 params.callback = callback 26551 return params 26552} 26553 26554function createXHR(uri, options, callback) { 26555 options = initParams(uri, options, callback) 26556 return _createXHR(options) 26557} 26558 26559function _createXHR(options) { 26560 if(typeof options.callback === "undefined"){ 26561 throw new Error("callback argument missing") 26562 } 26563 26564 var called = false 26565 var callback = function cbOnce(err, response, body){ 26566 if(!called){ 26567 called = true 26568 options.callback(err, response, body) 26569 } 26570 } 26571 26572 function readystatechange() { 26573 if (xhr.readyState === 4) { 26574 loadFunc() 26575 } 26576 } 26577 26578 function getBody() { 26579 // Chrome with requestType=blob throws errors arround when even testing access to responseText 26580 var body = undefined 26581 26582 if (xhr.response) { 26583 body = xhr.response 26584 } else { 26585 body = xhr.responseText || getXml(xhr) 26586 } 26587 26588 if (isJson) { 26589 try { 26590 body = JSON.parse(body) 26591 } catch (e) {} 26592 } 26593 26594 return body 26595 } 26596 26597 var failureResponse = { 26598 body: undefined, 26599 headers: {}, 26600 statusCode: 0, 26601 method: method, 26602 url: uri, 26603 rawRequest: xhr 26604 } 26605 26606 function errorFunc(evt) { 26607 clearTimeout(timeoutTimer) 26608 if(!(evt instanceof Error)){ 26609 evt = new Error("" + (evt || "Unknown XMLHttpRequest Error") ) 26610 } 26611 evt.statusCode = 0 26612 return callback(evt, failureResponse) 26613 } 26614 26615 // will load the data & process the response in a special response object 26616 function loadFunc() { 26617 if (aborted) return 26618 var status 26619 clearTimeout(timeoutTimer) 26620 if(options.useXDR && xhr.status===undefined) { 26621 //IE8 CORS GET successful response doesn't have a status field, but body is fine 26622 status = 200 26623 } else { 26624 status = (xhr.status === 1223 ? 204 : xhr.status) 26625 } 26626 var response = failureResponse 26627 var err = null 26628 26629 if (status !== 0){ 26630 response = { 26631 body: getBody(), 26632 statusCode: status, 26633 method: method, 26634 headers: {}, 26635 url: uri, 26636 rawRequest: xhr 26637 } 26638 if(xhr.getAllResponseHeaders){ //remember xhr can in fact be XDR for CORS in IE 26639 response.headers = parseHeaders(xhr.getAllResponseHeaders()) 26640 } 26641 } else { 26642 err = new Error("Internal XMLHttpRequest Error") 26643 } 26644 return callback(err, response, response.body) 26645 } 26646 26647 var xhr = options.xhr || null 26648 26649 if (!xhr) { 26650 if (options.cors || options.useXDR) { 26651 xhr = new createXHR.XDomainRequest() 26652 }else{ 26653 xhr = new createXHR.XMLHttpRequest() 26654 } 26655 } 26656 26657 var key 26658 var aborted 26659 var uri = xhr.url = options.uri || options.url 26660 var method = xhr.method = options.method || "GET" 26661 var body = options.body || options.data || null 26662 var headers = xhr.headers = options.headers || {} 26663 var sync = !!options.sync 26664 var isJson = false 26665 var timeoutTimer 26666 26667 if ("json" in options) { 26668 isJson = true 26669 headers["accept"] || headers["Accept"] || (headers["Accept"] = "application/json") //Don't override existing accept header declared by user 26670 if (method !== "GET" && method !== "HEAD") { 26671 headers["content-type"] || headers["Content-Type"] || (headers["Content-Type"] = "application/json") //Don't override existing accept header declared by user 26672 body = JSON.stringify(options.json) 26673 } 26674 } 26675 26676 xhr.onreadystatechange = readystatechange 26677 xhr.onload = loadFunc 26678 xhr.onerror = errorFunc 26679 // IE9 must have onprogress be set to a unique function. 26680 xhr.onprogress = function () { 26681 // IE must die 26682 } 26683 xhr.ontimeout = errorFunc 26684 xhr.open(method, uri, !sync, options.username, options.password) 26685 //has to be after open 26686 if(!sync) { 26687 xhr.withCredentials = !!options.withCredentials 26688 } 26689 // Cannot set timeout with sync request 26690 // not setting timeout on the xhr object, because of old webkits etc. not handling that correctly 26691 // both npm's request and jquery 1.x use this kind of timeout, so this is being consistent 26692 if (!sync && options.timeout > 0 ) { 26693 timeoutTimer = setTimeout(function(){ 26694 aborted=true//IE9 may still call readystatechange 26695 xhr.abort("timeout") 26696 var e = new Error("XMLHttpRequest timeout") 26697 e.code = "ETIMEDOUT" 26698 errorFunc(e) 26699 }, options.timeout ) 26700 } 26701 26702 if (xhr.setRequestHeader) { 26703 for(key in headers){ 26704 if(headers.hasOwnProperty(key)){ 26705 xhr.setRequestHeader(key, headers[key]) 26706 } 26707 } 26708 } else if (options.headers && !isEmpty(options.headers)) { 26709 throw new Error("Headers cannot be set on an XDomainRequest object") 26710 } 26711 26712 if ("responseType" in options) { 26713 xhr.responseType = options.responseType 26714 } 26715 26716 if ("beforeSend" in options && 26717 typeof options.beforeSend === "function" 26718 ) { 26719 options.beforeSend(xhr) 26720 } 26721 26722 xhr.send(body) 26723 26724 return xhr 26725 26726 26727} 26728 26729function getXml(xhr) { 26730 if (xhr.responseType === "document") { 26731 return xhr.responseXML 26732 } 26733 var firefoxBugTakenEffect = xhr.status === 204 && xhr.responseXML && xhr.responseXML.documentElement.nodeName === "parsererror" 26734 if (xhr.responseType === "" && !firefoxBugTakenEffect) { 26735 return xhr.responseXML 26736 } 26737 26738 return null 26739} 26740 26741function noop() {} 26742 26743}
26743,{"105":105,"108":108,"109":109,"95":95}],105:[function(_dereq_,module,exports){ 26744module.exports = isFunction 26745 26746var toString = Object.prototype.toString 26747 26748function isFunction (fn) { 26749 var string = toString.call(fn) 26750 return string === '[object Function]' || 26751 (typeof fn === 'function' && string !== '[object RegExp]') || 26752 (typeof window !== 'undefined' && 26753 // IE8 and below 26754 (fn === window.setTimeout || 26755 fn === window.alert || 26756 fn === window.confirm || 26757 fn === window.prompt)) 26758}; 26759 26760},{}],106:[function(_dereq_,module,exports){ 26761var isFunction = _dereq_(105) 26762 26763module.exports = forEach 26764 26765var toString = Object.prototype.toString 26766var hasOwnProperty = Object.prototype.hasOwnProperty 26767 26768function forEach(list, iterator, context) { 26769 if (!isFunction(iterator)) { 26770 throw new TypeError('iterator must be a function') 26771 } 26772 26773 if (arguments.length < 3) { 26774 context = this 26775 } 26776 26777 if (toString.call(list) === '[object Array]') 26778 forEachArray(list, iterator, context) 26779 else if (typeof list === 'string') 26780 forEachString(list, iterator, context) 26781 else 26782 forEachObject(list, iterator, context) 26783} 26784 26785function forEachArray(array, iterator, context) { 26786 for (var i = 0, len = array.length; i < len; i++) { 26787 if (hasOwnProperty.call(array, i)) { 26788 iterator.call(context, array[i], i, array) 26789 } 26790 } 26791} 26792 26793function forEachString(string, iterator, context) { 26794 for (var i = 0, len = string.length; i < len; i++) { 26795 // no such thing as a sparse string. 26796 iterator.call(context, string.charAt(i), i, string) 26797 } 26798} 26799 26800function forEachObject(object, iterator, context) { 26801 for (var k in object) { 26802 if (hasOwnProperty.call(object, k)) { 26803 iterator.call(context, object[k], k, object) 26804 } 26805 } 26806} 26807 26808},{"105":105}],107:[function(_dereq_,module,exports){ 26809 26810exports = module.exports = trim; 26811 26812function trim(str){ 26813 return str.replace(/^\s*|\s*$/g, ''); 26814} 26815 26816exports.left = function(str){ 26817 return str.replace(/^\s*/, ''); 26818}; 26819 26820exports.right = function(str){ 26821 return str.replace(/\s*$/, ''); 26822}; 26823 26824},{}],108:[function(_dereq_,module,exports){ 26825var trim = _dereq_(107) 26826 , forEach = _dereq_(106) 26827 , isArray = function(arg) { 26828 return Object.prototype.toString.call(arg) === '[object Array]'; 26829 } 26830 26831module.exports = function (headers) { 26832 if (!headers) 26833 return {} 26834 26835 var result = {} 26836 26837 forEach( 26838 trim(headers).split('\n') 26839 , function (row) { 26840 var index = row.indexOf(':') 26841 , key = trim(row.slice(0, index)).toLowerCase() 26842 , value = trim(row.slice(index + 1)) 26843 26844 if (typeof(result[key]) === 'undefined') { 26845 result[key] = value 26846 } else if (isArray(result[key])) { 26847 result[key].push(value) 26848 } else { 26849 result[key] = [ result[key], value ] 26850 } 26851 } 26852 ) 26853 26854 return result 26855} 26856},{"106":106,"107":107}],109:[function(_dereq_,module,exports){ 26857module.exports = extend 26858 26859var hasOwnProperty = Object.prototype.hasOwnProperty; 26860 26861function extend() { 26862 var target = {} 26863 26864 for (var i = 0; i < arguments.length; i++) { 26865 var source = arguments[i] 26866 26867 for (var key in source) { 26868 if (hasOwnProperty.call(source, key)) { 26869 target[key] = source[key] 26870 } 26871 } 26872 } 26873 26874 return target 26875} 26876 26877},{}]},{},[93])(93) 26878}); 26879/** 26880 * videojs-contrib-hls 26881 * @version 5.11.0 26882 * @copyright 2017 Brightcove, Inc 26883 * @license Apache-2.0 26884 */ 26885(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.videojsContribHls = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ 26886/** 26887 * @file ad-cue-tags.js 26888 */ 26889'use strict'; 26890 26891Object.defineProperty(exports, '__esModule', { 26892 value: true 26893}); 26894 26895var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); }
26895 else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })(); 26896 26897function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 26898 26899var _globalWindow = require('global/window'); 26900 26901var _globalWindow2 = _interopRequireDefault(_globalWindow); 26902 26903/** 26904 * Searches for an ad cue that overlaps with the given mediaTime 26905 */ 26906var findAdCue = function findAdCue(track, mediaTime) { 26907 var cues = track.cues; 26908 26909 for (var i = 0; i < cues.length; i++) { 26910 var cue = cues[i]; 26911 26912 if (mediaTime >= cue.adStartTime && mediaTime <= cue.adEndTime) { 26913 return cue; 26914 } 26915 } 26916 return null; 26917}; 26918 26919var updateAdCues = function updateAdCues(media, track) { 26920 var offset = arguments.length <= 2 || arguments[2] === undefined ? 0 : arguments[2]; 26921 26922 if (!media.segments) { 26923 return; 26924 } 26925 26926 var mediaTime = offset; 26927 var cue = undefined; 26928 26929 for (var i = 0; i < media.segments.length; i++) { 26930 var segment = media.segments[i]; 26931 26932 if (!cue) { 26933 // Since the cues will span for at least the segment duration, adding a fudge 26934 // factor of half segment duration will prevent duplicate cues from being 26935 // created when timing info is not exact (e.g. cue start time initialized 26936 // at 10.006677, but next call mediaTime is 10.003332 ) 26937 cue = findAdCue(track, mediaTime + segment.duration / 2); 26938 } 26939 26940 if (cue) { 26941 if ('cueIn' in segment) { 26942 // Found a CUE-IN so end the cue 26943 cue.endTime = mediaTime; 26944 cue.adEndTime = mediaTime; 26945 mediaTime += segment.duration; 26946 cue = null; 26947 continue; 26948 } 26949 26950 if (mediaTime < cue.endTime) { 26951 // Already processed this mediaTime for this cue 26952 mediaTime += segment.duration; 26953 continue; 26954 } 26955 26956 // otherwise extend cue until a CUE-IN is found 26957 cue.endTime += segment.duration; 26958 } else { 26959 if ('cueOut' in segment) { 26960 cue = new _globalWindow2['default'].VTTCue(mediaTime, mediaTime + segment.duration, segment.cueOut); 26961 cue.adStartTime = mediaTime; 26962 // Assumes tag format to be 26963 // #EXT-X-CUE-OUT:30 26964 cue.adEndTime = mediaTime + parseFloat(segment.cueOut); 26965 track.addCue(cue); 26966 } 26967 26968 if ('cueOutCont' in segment) { 26969 // Entered into the middle of an ad cue 26970 var adOffset = undefined; 26971 var adTotal = undefined; 26972 26973 // Assumes tag formate to be 26974 // #EXT-X-CUE-OUT-CONT:10/30 26975 26976 var _segment$cueOutCont$split$map = segment.cueOutCont.split('/').map(parseFloat); 26977 26978 var _segment$cueOutCont$split$map2 = _slicedToArray(_segment$cueOutCont$split$map, 2); 26979 26980 adOffset = _segment$cueOutCont$split$map2[0]; 26981 adTotal = _segment$cueOutCont$split$map2[1]; 26982 26983 cue = new _globalWindow2['default'].VTTCue(mediaTime, mediaTime + segment.duration, ''); 26984 cue.adStartTime = mediaTime - adOffset; 26985 cue.adEndTime = cue.adStartTime + adTotal; 26986 track.addCue(cue); 26987 } 26988 } 26989 mediaTime += segment.duration; 26990 } 26991}; 26992 26993exports['default'] = { 26994 updateAdCues: updateAdCues, 26995 findAdCue: findAdCue 26996}; 26997module.exports = exports['default']; 26998},{"global/window":32}],2:[function(require,module,exports){ 26999/** 27000 * @file bin-utils.js 27001 */ 27002 27003/** 27004 * convert a TimeRange to text 27005 * 27006 * @param {TimeRange} range the timerange to use for conversion 27007 * @param {Number} i the iterator on the range to convert 27008 */ 27009'use strict'; 27010 27011Object.defineProperty(exports, '__esModule', { 27012 value: true 27013}); 27014var textRange = function textRange(range, i) { 27015 return range.start(i) + '-' + range.end(i); 27016}; 27017 27018/** 27019 * format a number as hex string 27020 * 27021 * @param {Number} e The number 27022 * @param {Number} i the iterator 27023 */ 27024var formatHexString = function formatHexString(e, i) { 27025 var value = e.toString(16); 27026 27027 return '00'.substring(0, 2 - value.length) + value + (i % 2 ? ' ' : ''); 27028}; 27029var formatAsciiString = function formatAsciiString(e) { 27030 if (e >= 0x20 && e < 0x7e) { 27031 return String.fromCharCode(e); 27032 } 27033 return '.'; 27034}; 27035 27036/** 27037 * Creates an object for sending to a web worker modifying properties that are TypedArrays 27038 * into a new object with seperated properties for the buffer, byteOffset, and byteLength. 27039 * 27040 * @param {Object} message 27041 * Object of properties and values to send to the web worker 27042 * @return {Object} 27043 * Modified message with TypedArray values expanded 27044 * @function createTransferableMessage 27045 */ 27046var createTransferableMessage = function createTransferableMessage(message) { 27047 var transferable = {}; 27048 27049 Object.keys(message).forEach(function (key) { 27050 var value = message[key]; 27051 27052 if (ArrayBuffer.isView(value)) { 27053 transferable[key] = { 27054 bytes: value.buffer, 27055 byteOffset: value.byteOffset, 27056 byteLength: value.byteLength 27057 }; 27058 } else { 27059 transferable[key] = value; 27060 } 27061 }); 27062 27063 return transferable; 27064}; 27065 27066/** 27067 * Returns a unique string identifier for a media initialization 27068 * segment. 27069 */ 27070var initSegmentId = function initSegmentId(initSegment) { 27071 var byterange = initSegment.byterange || { 27072 length: Infinity, 27073 offset: 0 27074 }; 27075 27076 return [byterange.length, byterange.offset, initSegment.resolvedUri].join(','); 27077}; 27078 27079/** 27080 * utils to help dump binary data to the console 27081 */ 27082var utils = { 27083 hexDump: function hexDump(data) { 27084 var bytes = Array.prototype.slice.call(data); 27085 var step = 16; 27086 var result = ''; 27087 var hex = undefined; 27088 var ascii = undefined; 27089 27090 for (var j = 0;
27090 j < bytes.length / step; j++) { 27091 hex = bytes.slice(j * step, j * step + step).map(formatHexString).join(''); 27092 ascii = bytes.slice(j * step, j * step + step).map(formatAsciiString).join(''); 27093 result += hex + ' ' + ascii + '\n'; 27094 } 27095 return result; 27096 }, 27097 tagDump: function tagDump(tag) { 27098 return utils.hexDump(tag.bytes); 27099 }, 27100 textRanges: function textRanges(ranges) { 27101 var result = ''; 27102 var i = undefined; 27103 27104 for (i = 0; i < ranges.length; i++) { 27105 result += textRange(ranges, i) + ' '; 27106 } 27107 return result; 27108 }, 27109 createTransferableMessage: createTransferableMessage, 27110 initSegmentId: initSegmentId 27111}; 27112 27113exports['default'] = utils; 27114module.exports = exports['default']; 27115},{}],3:[function(require,module,exports){ 27116"use strict"; 27117 27118Object.defineProperty(exports, "__esModule", { 27119 value: true 27120}); 27121exports["default"] = { 27122 GOAL_BUFFER_LENGTH: 30, 27123 MAX_GOAL_BUFFER_LENGTH: 60, 27124 GOAL_BUFFER_LENGTH_RATE: 1, 27125 // A fudge factor to apply to advertised playlist bitrates to account for 27126 // temporary flucations in client bandwidth 27127 BANDWIDTH_VARIANCE: 1.2, 27128 // How much of the buffer must be filled before we consider upswitching 27129 BUFFER_LOW_WATER_LINE: 0, 27130 MAX_BUFFER_LOW_WATER_LINE: 30, 27131 BUFFER_LOW_WATER_LINE_RATE: 1 27132}; 27133module.exports = exports["default"]; 27134},{}],4:[function(require,module,exports){ 27135'use strict'; 27136 27137Object.defineProperty(exports, '__esModule', { 27138 value: true 27139}); 27140 27141function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 27142 27143var _globalWindow = require('global/window'); 27144 27145var _globalWindow2 = _interopRequireDefault(_globalWindow); 27146 27147var _aesDecrypter = require('aes-decrypter'); 27148 27149var _binUtils = require('./bin-utils'); 27150 27151/** 27152 * Our web worker interface so that things can talk to aes-decrypter 27153 * that will be running in a web worker. the scope is passed to this by 27154 * webworkify. 27155 * 27156 * @param {Object} self 27157 * the scope for the web worker 27158 */ 27159var DecrypterWorker = function DecrypterWorker(self) { 27160 self.onmessage = function (event) { 27161 var data = event.data; 27162 var encrypted = new Uint8Array(data.encrypted.bytes, data.encrypted.byteOffset, data.encrypted.byteLength); 27163 var key = new Uint32Array(data.key.bytes, data.key.byteOffset, data.key.byteLength / 4); 27164 var iv = new Uint32Array(data.iv.bytes, data.iv.byteOffset, data.iv.byteLength / 4); 27165 27166 /* eslint-disable no-new, handle-callback-err */ 27167 new _aesDecrypter.Decrypter(encrypted, key, iv, function (err, bytes) { 27168 _globalWindow2['default'].postMessage((0, _binUtils.createTransferableMessage)({ 27169 source: data.source, 27170 decrypted: bytes 27171 }), [bytes.buffer]); 27172 }); 27173 /* eslint-enable */ 27174 }; 27175}; 27176 27177exports['default'] = function (self) { 27178 return new DecrypterWorker(self); 27179}; 27180 27181module.exports = exports['default']; 27182},{"./bin-utils":2,"aes-decrypter":25,"global/window":32}],5:[function(require,module,exports){ 27183(function (global){ 27184/** 27185 * @file master-playlist-controller.js 27186 */ 27187'use strict'; 27188 27189Object.defineProperty(exports, '__esModule', { 27190 value: true 27191}); 27192 27193var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 27194 27195var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 27196 27197function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 27198 27199function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 27200 27201function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
27201onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 27202 27203var _playlistLoader = require('./playlist-loader'); 27204 27205var _playlistLoader2 = _interopRequireDefault(_playlistLoader); 27206 27207var _segmentLoader = require('./segment-loader'); 27208 27209var _segmentLoader2 = _interopRequireDefault(_segmentLoader); 27210 27211var _vttSegmentLoader = require('./vtt-segment-loader'); 27212 27213var _vttSegmentLoader2 = _interopRequireDefault(_vttSegmentLoader); 27214 27215var _ranges = require('./ranges'); 27216 27217var _ranges2 = _interopRequireDefault(_ranges); 27218 27219var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null); 27220 27221var _videoJs2 = _interopRequireDefault(_videoJs); 27222 27223var _adCueTags = require('./ad-cue-tags'); 27224 27225var _adCueTags2 = _interopRequireDefault(_adCueTags); 27226 27227var _syncController = require('./sync-controller'); 27228 27229var _syncController2 = _interopRequireDefault(_syncController); 27230 27231var _videojsContribMediaSourcesEs5CodecUtils = require('videojs-contrib-media-sources/es5/codec-utils'); 27232 27233var _webworkify = require('webworkify'); 27234 27235var _webworkify2 = _interopRequireDefault(_webworkify); 27236 27237var _decrypterWorker = require('./decrypter-worker'); 27238 27239var _decrypterWorker2 = _interopRequireDefault(_decrypterWorker); 27240 27241var _config = require('./config'); 27242 27243var _config2 = _interopRequireDefault(_config); 27244 27245var _utilCodecsJs = require('./util/codecs.js'); 27246 27247var _mediaGroups = require('./media-groups'); 27248 27249var ABORT_EARLY_BLACKLIST_SECONDS = 60 * 2; 27250 27251var Hls = undefined; 27252 27253// Default codec parameters if none were provided for video and/or audio 27254var defaultCodecs = { 27255 videoCodec: 'avc1', 27256 videoObjectTypeIndicator: '.4d400d', 27257 // AAC-LC 27258 audioProfile: '2' 27259}; 27260 27261// SegmentLoader stats that need to have each loader's 27262// values summed to calculate the final value 27263var loaderStats = ['mediaRequests', 'mediaRequestsAborted', 'mediaRequestsTimedout', 'mediaRequestsErrored', 'mediaTransferDuration', 'mediaBytesTransferred']; 27264var sumLoaderStat = function sumLoaderStat(stat) { 27265 return this.audioSegmentLoader_[stat] + this.mainSegmentLoader_[stat]; 27266}; 27267 27268/** 27269 * Replace codecs in the codec string with the old apple-style `avc1.<dd>.<dd>` to the 27270 * standard `avc1.<hhhhhh>`. 27271 * 27272 * @param codecString {String} the codec string 27273 * @return {String} the codec string with old apple-style codecs replaced 27274 * 27275 * @private 27276 */ 27277var mapLegacyAvcCodecs_ = function mapLegacyAvcCodecs_(codecString) { 27278 return codecString.replace(/avc1\.(\d+)\.(\d+)/i, function (match) { 27279 return (0, _videojsContribMediaSourcesEs5CodecUtils.translateLegacyCodecs)([match])[0]; 27280 }); 27281}; 27282 27283exports.mapLegacyAvcCodecs_ = mapLegacyAvcCodecs_; 27284/** 27285 * Build a media mime-type string from a set of parameters 27286 * @param {String} type either 'audio' or 'video' 27287 * @param {String} container either 'mp2t' or 'mp4' 27288 * @param {Array} codecs an array of codec strings to add 27289 * @return {String} a valid media mime-type 27290 */ 27291var makeMimeTypeString = function makeMimeTypeString(type, container, codecs) { 27292 // The codecs array is filtered so that falsey values are 27293 // dropped and don't cause Array#join to create spurious 27294 // commas 27295 return type + '/' + container + '; codecs="' + codecs.filter(function (c) { 27296 return !!c; 27297 }).join(', ') + '"'; 27298}; 27299 27300/** 27301 * Returns the type container based on information in the playlist 27302 * @param {Playlist} media the current media playlist 27303 * @return {String} a valid media container type 27304 */ 27305var getContainerType = function getContainerType(media) { 27306 // An initialization segment means the media playlist is an iframe 27307 // playlist or is using the mp4 container. We don't currently 27308 // support iframe playlists, so assume this is signalling mp4 27309 // fragments. 27310 if (media.segments && media.segments.length && media.segments[0].map) { 27311 return 'mp4'; 27312 } 27313 return 'mp2t'; 27314}; 27315 27316/** 27317 * Returns a set of codec strings parsed from the playlist or the default 27318 * codec strings if no codecs were specified in the playlist 27319 * @param {Playlist} media the current media playlist 27320 * @return {Object} an object with the video and audio codecs 27321 */ 27322var getCodecs = function getCodecs(media) { 27323 // if the codecs were explicitly specified, use them instead of the 27324 // defaults 27325 var mediaAttributes = media.attributes || {}; 27326 27327 if (mediaAttributes.CODECS) { 27328 return (0, _utilCodecsJs.parseCodecs)(mediaAttributes.CODECS); 27329 } 27330 return defaultCodecs; 27331}; 27332 27333/**
27334 * Calculates the MIME type strings for a working configuration of 27335 * SourceBuffers to play variant streams in a master playlist. If 27336 * there is no possible working configuration, an empty array will be 27337 * returned. 27338 * 27339 * @param master {Object} the m3u8 object for the master playlist 27340 * @param media {Object} the m3u8 object for the variant playlist 27341 * @return {Array} the MIME type strings. If the array has more than 27342 * one entry, the first element should be applied to the video 27343 * SourceBuffer and the second to the audio SourceBuffer. 27344 * 27345 * @private 27346 */ 27347var mimeTypesForPlaylist_ = function mimeTypesForPlaylist_(master, media) { 27348 var containerType = getContainerType(media); 27349 var codecInfo = getCodecs(media); 27350 var mediaAttributes = media.attributes || {}; 27351 // Default condition for a traditional HLS (no demuxed audio/video) 27352 var isMuxed = true; 27353 var isMaat = false; 27354 27355 if (!media) { 27356 // Not enough information 27357 return []; 27358 } 27359 27360 if (master.mediaGroups.AUDIO && mediaAttributes.AUDIO) { 27361 var audioGroup = master.mediaGroups.AUDIO[mediaAttributes.AUDIO]; 27362 27363 // Handle the case where we are in a multiple-audio track scenario 27364 if (audioGroup) { 27365 isMaat = true; 27366 // Start with the everything demuxed then... 27367 isMuxed = false; 27368 // ...check to see if any audio group tracks are muxed (ie. lacking a uri) 27369 for (var groupId in audioGroup) { 27370 if (!audioGroup[groupId].uri) { 27371 isMuxed = true; 27372 break; 27373 } 27374 } 27375 } 27376 } 27377 27378 // HLS with multiple-audio tracks must always get an audio codec. 27379 // Put another way, there is no way to have a video-only multiple-audio HLS! 27380 if (isMaat && !codecInfo.audioProfile) { 27381 _videoJs2['default'].log.warn('Multiple audio tracks present but no audio codec string is specified. ' + 'Attempting to use the default audio codec (mp4a.40.2)'); 27382 codecInfo.audioProfile = defaultCodecs.audioProfile; 27383 } 27384 27385 // Generate the final codec strings from the codec object generated above 27386 var codecStrings = {}; 27387 27388 if (codecInfo.videoCodec) { 27389 codecStrings.video = '' + codecInfo.videoCodec + codecInfo.videoObjectTypeIndicator; 27390 } 27391 27392 if (codecInfo.audioProfile) { 27393 codecStrings.audio = 'mp4a.40.' + codecInfo.audioProfile; 27394 } 27395 27396 // Finally, make and return an array with proper mime-types depending on 27397 // the configuration 27398 var justAudio = makeMimeTypeString('audio', containerType, [codecStrings.audio]); 27399 var justVideo = makeMimeTypeString('video', containerType, [codecStrings.video]); 27400 var bothVideoAudio = makeMimeTypeString('video', containerType, [codecStrings.video, codecStrings.audio]); 27401 27402 if (isMaat) { 27403 if (!isMuxed && codecStrings.video) { 27404 return [justVideo, justAudio]; 27405 } 27406 // There exists the possiblity that this will return a `video/container` 27407 // mime-type for the first entry in the array even when there is only audio. 27408 // This doesn't appear to be a problem and simplifies the code. 27409 return [bothVideoAudio, justAudio]; 27410 } 27411 27412 // If there is ano video codec at all, always just return a single 27413 // audio/<container> mime-type 27414 if (!codecStrings.video) { 27415 return [justAudio]; 27416 } 27417 27418 // When not using separate audio media groups, audio and video is 27419 // *always* muxed 27420 return [bothVideoAudio]; 27421}; 27422 27423exports.mimeTypesForPlaylist_ = mimeTypesForPlaylist_; 27424/** 27425 * the master playlist controller controller all interactons 27426 * between playlists and segmentloaders. At this time this mainly 27427 * involves a master playlist and a series of audio playlists 27428 * if they are available 27429 * 27430 * @class MasterPlaylistController 27431 * @extends videojs.EventTarget 27432 */ 27433 27434var MasterPlaylistController = (function (_videojs$EventTarget) { 27435 _inherits(MasterPlaylistController, _videojs$EventTarget); 27436 27437 function MasterPlaylistController(options) { 27438 var _this = this; 27439 27440 _classCallCheck(this, MasterPlaylistController); 27441 27442 _get(Object.getPrototypeOf(MasterPlaylistController.prototype), 'constructor', this).call(this); 27443 27444 var url = options.url; 27445 var withCredentials = options.withCredentials; 27446 var mode = options.mode; 27447 var tech = options.tech; 27448 var bandwidth = options.bandwidth; 27449 var externHls = options.externHls; 27450 var useCueTags = options.useCueTags;
27451 var blacklistDuration = options.blacklistDuration; 27452 var enableLowInitialPlaylist = options.enableLowInitialPlaylist; 27453 27454 if (!url) { 27455 throw new Error('A non-empty playlist URL is required'); 27456 } 27457 27458 Hls = externHls; 27459 27460 this.withCredentials = withCredentials; 27461 this.tech_ = tech; 27462 this.hls_ = tech.hls; 27463 this.mode_ = mode; 27464 this.useCueTags_ = useCueTags; 27465 this.blacklistDuration = blacklistDuration; 27466 this.enableLowInitialPlaylist = enableLowInitialPlaylist; 27467 if (this.useCueTags_) { 27468 this.cueTagsTrack_ = this.tech_.addTextTrack('metadata', 'ad-cues'); 27469 this.cueTagsTrack_.inBandMetadataTrackDispatchType = ''; 27470 } 27471 27472 this.requestOptions_ = { 27473 withCredentials: this.withCredentials, 27474 timeout: null 27475 }; 27476 27477 this.mediaTypes_ = (0, _mediaGroups.createMediaTypes)(); 27478 27479 this.mediaSource = new _videoJs2['default'].MediaSource({ mode: mode }); 27480 27481 // load the media source into the player 27482 this.mediaSource.addEventListener('sourceopen', this.handleSourceOpen_.bind(this)); 27483 27484 this.seekable_ = _videoJs2['default'].createTimeRanges(); 27485 this.hasPlayed_ = function () { 27486 return false; 27487 }; 27488 27489 this.syncController_ = new _syncController2['default'](options); 27490 this.segmentMetadataTrack_ = tech.addRemoteTextTrack({ 27491 kind: 'metadata', 27492 label: 'segment-metadata' 27493 }, false).track; 27494 27495 this.decrypter_ = (0, _webworkify2['default'])(_decrypterWorker2['default']); 27496 27497 var segmentLoaderSettings = { 27498 hls: this.hls_, 27499 mediaSource: this.mediaSource, 27500 currentTime: this.tech_.currentTime.bind(this.tech_), 27501 seekable: function seekable() { 27502 return _this.seekable(); 27503 }, 27504 seeking: function seeking() { 27505 return _this.tech_.seeking(); 27506 }, 27507 duration: function duration() { 27508 return _this.mediaSource.duration; 27509 }, 27510 hasPlayed: function hasPlayed() { 27511 return _this.hasPlayed_(); 27512 }, 27513 goalBufferLength: function goalBufferLength() { 27514 return _this.goalBufferLength(); 27515 }, 27516 bandwidth: bandwidth, 27517 syncController: this.syncController_, 27518 decrypter: this.decrypter_ 27519 }; 27520 27521 // setup playlist loaders 27522 this.masterPlaylistLoader_ = new _playlistLoader2['default'](url, this.hls_, this.withCredentials); 27523 this.setupMasterPlaylistLoaderListeners_(); 27524 27525 // setup segment loaders 27526 // combined audio/video or just video when alternate audio track is selected 27527 this.mainSegmentLoader_ = new _segmentLoader2['default'](_videoJs2['default'].mergeOptions(segmentLoaderSettings, { 27528 segmentMetadataTrack: this.segmentMetadataTrack_, 27529 loaderType: 'main' 27530 }), options); 27531 27532 // alternate audio track 27533 this.audioSegmentLoader_ = new _segmentLoader2['default'](_videoJs2['default'].mergeOptions(segmentLoaderSettings, { 27534 loaderType: 'audio' 27535 }), options); 27536 27537 this.subtitleSegmentLoader_ = new _vttSegmentLoader2['default'](_videoJs2['default'].mergeOptions(segmentLoaderSettings, { 27538 loaderType: 'vtt' 27539 }), options); 27540 27541 this.setupSegmentLoaderListeners_(); 27542 27543 // Create SegmentLoader stat-getters
vendor: 7,535 bytes, lines 27544-27753
27544 loaderStats.forEach(function (stat) { 27545 _this[stat + '_'] = sumLoaderStat.bind(_this, stat); 27546 }); 27547 27548 this.masterPlaylistLoader_.load(); 27549 } 27550 27551 /** 27552 * Register event handlers on the master playlist loader. A helper 27553 * function for construction time. 27554 * 27555 * @private 27556 */ 27557 27558 _createClass(MasterPlaylistController, [{ 27559 key: 'setupMasterPlaylistLoaderListeners_', 27560 value: function setupMasterPlaylistLoaderListeners_() { 27561 var _this2 = this; 27562 27563 this.masterPlaylistLoader_.on('loadedmetadata', function () { 27564 var media = _this2.masterPlaylistLoader_.media(); 27565 var requestTimeout = _this2.masterPlaylistLoader_.targetDuration * 1.5 * 1000; 27566 27567 // If we don't have any more available playlists, we don't want to 27568 // timeout the request. 27569 if (_this2.masterPlaylistLoader_.isLowestEnabledRendition_()) { 27570 _this2.requestOptions_.timeout = 0; 27571 } else { 27572 _this2.requestOptions_.timeout = requestTimeout; 27573 } 27574 27575 // if this isn't a live video and preload permits, start 27576 // downloading segments 27577 if (media.endList && _this2.tech_.preload() !== 'none') { 27578 _this2.mainSegmentLoader_.playlist(media, _this2.requestOptions_); 27579 _this2.mainSegmentLoader_.load(); 27580 } 27581 27582 (0, _mediaGroups.setupMediaGroups)({ 27583 segmentLoaders: { 27584 AUDIO: _this2.audioSegmentLoader_, 27585 SUBTITLES: _this2.subtitleSegmentLoader_, 27586 main: _this2.mainSegmentLoader_ 27587 }, 27588 tech: _this2.tech_, 27589 requestOptions: _this2.requestOptions_, 27590 masterPlaylistLoader: _this2.masterPlaylistLoader_, 27591 mode: _this2.mode_, 27592 hls: _this2.hls_, 27593 master: _this2.master(), 27594 mediaTypes: _this2.mediaTypes_, 27595 blacklistCurrentPlaylist: _this2.blacklistCurrentPlaylist.bind(_this2) 27596 }); 27597 27598 _this2.triggerPresenceUsage_(_this2.master(), media); 27599 27600 try { 27601 _this2.setupSourceBuffers_(); 27602 } catch (e) { 27603 _videoJs2['default'].log.warn('Failed to create SourceBuffers', e); 27604 return _this2.mediaSource.endOfStream('decode'); 27605 } 27606 _this2.setupFirstPlay(); 27607 27608 _this2.trigger('selectedinitialmedia'); 27609 }); 27610 27611 this.masterPlaylistLoader_.on('loadedplaylist', function () { 27612 var updatedPlaylist = _this2.masterPlaylistLoader_.media(); 27613 27614 if (!updatedPlaylist) { 27615 var selectedMedia = undefined; 27616 27617 if (_this2.enableLowInitialPlaylist) { 27618 selectedMedia = _this2.selectInitialPlaylist(); 27619 } 27620 27621 if (!selectedMedia) { 27622 selectedMedia = _this2.selectPlaylist(); 27623 } 27624 27625 _this2.initialMedia_ = selectedMedia; 27626 _this2.masterPlaylistLoader_.media(_this2.initialMedia_); 27627 return; 27628 } 27629 27630 if (_this2.useCueTags_) { 27631 _this2.updateAdCues_(updatedPlaylist); 27632 } 27633 27634 // TODO: Create a new event on the PlaylistLoader that signals 27635 // that the segments have changed in some way and use that to 27636 // update the SegmentLoader instead of doing it twice here and 27637 // on `mediachange` 27638 _this2.mainSegmentLoader_.playlist(updatedPlaylist, _this2.requestOptions_); 27639 _this2.updateDuration(); 27640 27641 // If the player isn't paused, ensure that the segment loader is running, 27642 // as it is possible that it was temporarily stopped while waiting for 27643 // a playlist (e.g., in case the playlist errored and we re-requested it). 27644 if (!_this2.tech_.paused()) { 27645 _this2.mainSegmentLoader_.load(); 27646 } 27647 27648 if (!updatedPlaylist.endList) { 27649 (function () { 27650 var addSeekableRange = function addSeekableRange() { 27651 var seekable = _this2.seekable(); 27652 27653 if (seekable.length !== 0) { 27654 _this2.mediaSource.addSeekableRange_(seekable.start(0), seekable.end(0)); 27655 } 27656 }; 27657 27658 if (_this2.duration() !== Infinity) { 27659 (function () { 27660 var onDurationchange = function onDurationchange() { 27661 if (_this2.duration() === Infinity) { 27662 addSeekableRange(); 27663 } else { 27664 _this2.tech_.one('durationchange', onDurationchange); 27665 } 27666 }; 27667 27668 _this2.tech_.one('durationchange', onDurationchange); 27669 })(); 27670 } else { 27671 addSeekableRange(); 27672 } 27673 })(); 27674 } 27675 }); 27676 27677 this.masterPlaylistLoader_.on('error', function () { 27678 _this2.blacklistCurrentPlaylist(_this2.masterPlaylistLoader_.error); 27679 }); 27680 27681 this.masterPlaylistLoader_.on('mediachanging', function () { 27682 _this2.mainSegmentLoader_.abort(); 27683 _this2.mainSegmentLoader_.pause(); 27684 }); 27685 27686 this.masterPlaylistLoader_.on('mediachange', function () { 27687 var media = _this2.masterPlaylistLoader_.media(); 27688 var requestTimeout = _this2.masterPlaylistLoader_.targetDuration * 1.5 * 1000; 27689 27690 // If we don't have any more available playlists, we don't want to 27691 // timeout the request. 27692 if (_this2.masterPlaylistLoader_.isLowestEnabledRendition_()) { 27693 _this2.requestOptions_.timeout = 0; 27694 } else { 27695 _this2.requestOptions_.timeout = requestTimeout; 27696 } 27697 27698 // TODO: Create a new event on the PlaylistLoader that signals 27699 // that the segments have changed in some way and use that to 27700 // update the SegmentLoader instead of doing it twice here and 27701 // on `loadedplaylist` 27702 _this2.mainSegmentLoader_.playlist(media, _this2.requestOptions_); 27703 _this2.mainSegmentLoader_.load(); 27704 27705 _this2.tech_.trigger({ 27706 type: 'mediachange', 27707 bubbles: true 27708 }); 27709 }); 27710 27711 this.masterPlaylistLoader_.on('playlistunchanged', function () { 27712 var updatedPlaylist = _this2.masterPlaylistLoader_.media(); 27713 var playlistOutdated = _this2.stuckAtPlaylistEnd_(updatedPlaylist); 27714 27715 if (playlistOutdated) { 27716 // Playlist has stopped updating and we're stuck at its end. Try to 27717 // blacklist it and switch to another playlist in the hope that that 27718 // one is updating (and give the player a chance to re-adjust to the 27719 // safe live point). 27720 _this2.blacklistCurrentPlaylist({ 27721 message: 'Playlist no longer updating.' 27722 }); 27723 // useful for monitoring QoS 27724 _this2.tech_.trigger('playliststuck'); 27725 } 27726 }); 27727 27728 this.masterPlaylistLoader_.on('renditiondisabled', function () { 27729 _this2.tech_.trigger({ type: 'usage', name: 'hls-rendition-disabled' }); 27730 }); 27731 this.masterPlaylistLoader_.on('renditionenabled', function () { 27732 _this2.tech_.trigger({ type: 'usage', name: 'hls-rendition-enabled' }); 27733 }); 27734 } 27735 27736 /** 27737 * A helper function for triggerring presence usage events once per source 27738 * 27739 * @private 27740 */ 27741 }, { 27742 key: 'triggerPresenceUsage_', 27743 value: function triggerPresenceUsage_(master, media) { 27744 var mediaGroups = master.mediaGroups || {}; 27745 var defaultDemuxed = true; 27746 var audioGroupKeys = Object.keys(mediaGroups.AUDIO); 27747 27748 for (var mediaGroup in mediaGroups.AUDIO) { 27749 for (var label in mediaGroups.AUDIO[mediaGroup]) { 27750 var properties = mediaGroups.AUDIO[mediaGroup][label]; 27751 27752 if (!properties.uri) { 27753 defaultDemuxed = false;
27754 } 27755 } 27756 } 27757 27758 if (defaultDemuxed) { 27759 this.tech_.trigger({ type: 'usage', name: 'hls-demuxed' }); 27760 } 27761 27762 if (Object.keys(mediaGroups.SUBTITLES).length) { 27763 this.tech_.trigger({ type: 'usage', name: 'hls-webvtt' }); 27764 } 27765 27766 if (Hls.Playlist.isAes(media)) { 27767 this.tech_.trigger({ type: 'usage', name: 'hls-aes' }); 27768 } 27769 27770 if (Hls.Playlist.isFmp4(media)) { 27771 this.tech_.trigger({ type: 'usage', name: 'hls-fmp4' }); 27772 } 27773 27774 if (audioGroupKeys.length && Object.keys(mediaGroups.AUDIO[audioGroupKeys[0]]).length > 1) { 27775 this.tech_.trigger({ type: 'usage', name: 'hls-alternate-audio' }); 27776 } 27777 27778 if (this.useCueTags_) { 27779 this.tech_.trigger({ type: 'usage', name: 'hls-playlist-cue-tags' }); 27780 } 27781 } 27782 27783 /** 27784 * Register event handlers on the segment loaders. A helper function 27785 * for construction time. 27786 * 27787 * @private 27788 */ 27789 }, { 27790 key: 'setupSegmentLoaderListeners_', 27791 value: function setupSegmentLoaderListeners_() { 27792 var _this3 = this; 27793 27794 this.mainSegmentLoader_.on('bandwidthupdate', function () { 27795 var nextPlaylist = _this3.selectPlaylist(); 27796 var currentPlaylist = _this3.masterPlaylistLoader_.media(); 27797 var buffered = _this3.tech_.buffered(); 27798 var forwardBuffer = buffered.length ? buffered.end(buffered.length - 1) - _this3.tech_.currentTime() : 0; 27799 27800 var bufferLowWaterLine = _this3.bufferLowWaterLine(); 27801 27802 // If the playlist is live, then we want to not take low water line into account. 27803 // This is because in LIVE, the player plays 3 segments from the end of the 27804 // playlist, and if `BUFFER_LOW_WATER_LINE` is greater than the duration availble 27805 // in those segments, a viewer will never experience a rendition upswitch. 27806 if (!currentPlaylist.endList || 27807 // For the same reason as LIVE, we ignore the low water line when the VOD 27808 // duration is below the max potential low water line 27809 _this3.duration() < _config2['default'].MAX_BUFFER_LOW_WATER_LINE || 27810 // we want to switch down to lower resolutions quickly to continue playback, but 27811 nextPlaylist.attributes.BANDWIDTH < currentPlaylist.attributes.BANDWIDTH || 27812 // ensure we have some buffer before we switch up to prevent us running out of 27813 // buffer while loading a higher rendition. 27814 forwardBuffer >= bufferLowWaterLine) { 27815 _this3.masterPlaylistLoader_.media(nextPlaylist); 27816 } 27817 27818 _this3.tech_.trigger('bandwidthupdate'); 27819 }); 27820 this.mainSegmentLoader_.on('progress', function () { 27821 _this3.trigger('progress'); 27822 }); 27823 27824 this.mainSegmentLoader_.on('error', function () { 27825 _this3.blacklistCurrentPlaylist(_this3.mainSegmentLoader_.error()); 27826 }); 27827 27828 this.mainSegmentLoader_.on('syncinfoupdate', function () { 27829 _this3.onSyncInfoUpdate_(); 27830 }); 27831 27832 this.mainSegmentLoader_.on('timestampoffset', function () { 27833 _this3.tech_.trigger({ type: 'usage', name: 'hls-timestamp-offset' }); 27834 }); 27835 this.audioSegmentLoader_.on('syncinfoupdate', function () { 27836 _this3.onSyncInfoUpdate_(); 27837 }); 27838 27839 this.mainSegmentLoader_.on('ended', function () { 27840 _this3.onEndOfStream(); 27841 }); 27842 27843 this.mainSegmentLoader_.on('earlyabort', function () { 27844 _this3.blacklistCurrentPlaylist({ 27845 message: 'Aborted early because there isn\'t enough bandwidth to complete the ' + 'request without rebuffering.' 27846 }, ABORT_EARLY_BLACKLIST_SECONDS); 27847 }); 27848 27849 this.mainSegmentLoader_.on('reseteverything', function () { 27850 _this3.tech_.trigger('hls-reset'); 27851 }); 27852 27853 this.audioSegmentLoader_.on('ended', function () { 27854 _this3.onEndOfStream(); 27855 }); 27856 } 27857 }, { 27858 key: 'mediaSecondsLoaded_', 27859 value: function mediaSecondsLoaded_() { 27860 return Math.max(this.audioSegmentLoader_.mediaSecondsLoaded + this.mainSegmentLoader_.mediaSecondsLoaded); 27861 } 27862 27863 /** 27864 * Call load on our SegmentLoaders 27865 */ 27866 }, { 27867 key: 'load', 27868 value: function load() { 27869 this.mainSegmentLoader_.load(); 27870 if (this.mediaTypes_.AUDIO.activePlaylistLoader) { 27871 this.audioSegmentLoader_.load(); 27872 } 27873 if (this.mediaTypes_.SUBTITLES.activePlaylistLoader) { 27874 this.subtitleSegmentLoader_.load(); 27875 } 27876 } 27877 27878 /** 27879 * Re-tune playback quality level for the current player 27880 * conditions. This method may perform destructive actions, like 27881 * removing already buffered content, to readjust the currently 27882 * active playlist quickly. 27883 * 27884 * @private 27885 */ 27886 }, { 27887 key: 'fastQualityChange_', 27888 value: function fastQualityChange_() { 27889 var media = this.selectPlaylist(); 27890 27891 if (media !== this.masterPlaylistLoader_.media()) { 27892 this.masterPlaylistLoader_.media(media); 27893 27894 this.mainSegmentLoader_.resetLoader(); 27895 // don't need to reset audio as it is reset when media changes 27896 } 27897 } 27898 27899 /** 27900 * Begin playback. 27901 */ 27902 }, { 27903 key: 'play', 27904 value: function play() { 27905 if (this.setupFirstPlay()) { 27906 return; 27907 } 27908 27909 if (this.tech_.ended()) { 27910 this.tech_.setCurrentTime(0); 27911 } 27912 27913 if (this.hasPlayed_()) { 27914 this.load(); 27915 } 27916 27917 var seekable = this.tech_.seekable(); 27918 27919 // if the viewer has paused and we fell out of the live window, 27920 // seek forward to the live point 27921 if (this.tech_.duration() === Infinity) { 27922 if (this.tech_.currentTime() < seekable.start(0)) { 27923 return this.tech_.setCurrentTime(seekable.end(seekable.length - 1)); 27924 } 27925 } 27926 } 27927 27928 /** 27929 * Seek to the latest media position if this is a live video and the 27930 * player and video are loaded and initialized. 27931 */ 27932 }, { 27933 key: 'setupFirstPlay', 27934 value: function setupFirstPlay() { 27935 var seekable = undefined; 27936 var media = this.masterPlaylistLoader_.media(); 27937 27938 // check that everything is ready to begin buffering in the live 27939 // scenario 27940 // 1) the active media playlist is available 27941 if (media && 27942 // 2) the player is not paused 27943 !this.tech_.paused() && 27944 // 3) the player has not started playing 27945 !this.hasPlayed_()) { 27946 27947 // when the video is a live stream 27948 if (!media.endList) { 27949 this.trigger('firstplay'); 27950 27951 // seek to the latest media position for live videos 27952 seekable = this.seekable(); 27953 if (seekable.length) { 27954 this.tech_.setCurrentTime(seekable.end(0)); 27955 } 27956 } 27957 this.hasPlayed_ = function () { 27958 return true; 27959 }; 27960 // now that we are ready, load the segment 27961 this.load(); 27962 return true; 27963 } 27964 return false;
27965 } 27966 27967 /** 27968 * handle the sourceopen event on the MediaSource 27969 * 27970 * @private 27971 */ 27972 }, { 27973 key: 'handleSourceOpen_', 27974 value: function handleSourceOpen_() { 27975 // Only attempt to create the source buffer if none already exist. 27976 // handleSourceOpen is also called when we are "re-opening" a source buffer 27977 // after `endOfStream` has been called (in response to a seek for instance) 27978 try { 27979 this.setupSourceBuffers_(); 27980 } catch (e) { 27981 _videoJs2['default'].log.warn('Failed to create Source Buffers', e); 27982 return this.mediaSource.endOfStream('decode'); 27983 } 27984 27985 // if autoplay is enabled, begin playback. This is duplicative of 27986 // code in video.js but is required because play() must be invoked 27987 // *after* the media source has opened. 27988 if (this.tech_.autoplay()) { 27989 this.tech_.play(); 27990 } 27991 27992 this.trigger('sourceopen'); 27993 } 27994 27995 /** 27996 * Calls endOfStream on the media source when all active stream types have called 27997 * endOfStream 27998 * 27999 * @param {string} streamType 28000 * Stream type of the segment loader that called endOfStream 28001 * @private 28002 */ 28003 }, { 28004 key: 'onEndOfStream', 28005 value: function onEndOfStream() { 28006 var isEndOfStream = this.mainSegmentLoader_.ended_; 28007 28008 if (this.mediaTypes_.AUDIO.activePlaylistLoader) { 28009 // if the audio playlist loader exists, then alternate audio is active, so we need 28010 // to wait for both the main and audio segment loaders to call endOfStream 28011 isEndOfStream = isEndOfStream && this.audioSegmentLoader_.ended_; 28012 } 28013 28014 if (isEndOfStream) { 28015 this.mediaSource.endOfStream(); 28016 } 28017 } 28018 28019 /** 28020 * Check if a playlist has stopped being updated 28021 * @param {Object} playlist the media playlist object 28022 * @return {boolean} whether the playlist has stopped being updated or not 28023 */ 28024 }, { 28025 key: 'stuckAtPlaylistEnd_', 28026 value: function stuckAtPlaylistEnd_(playlist) { 28027 var seekable = this.seekable(); 28028 28029 if (!seekable.length) { 28030 // playlist doesn't have enough information to determine whether we are stuck 28031 return false; 28032 } 28033 28034 var expired = this.syncController_.getExpiredTime(playlist, this.mediaSource.duration); 28035 28036 if (expired === null) { 28037 return false; 28038 } 28039 28040 // does not use the safe live end to calculate playlist end, since we 28041 // don't want to say we are stuck while there is still content 28042 var absolutePlaylistEnd = Hls.Playlist.playlistEnd(playlist, expired); 28043 var currentTime = this.tech_.currentTime(); 28044 var buffered = this.tech_.buffered(); 28045 28046 if (!buffered.length) { 28047 // return true if the playhead reached the absolute end of the playlist 28048 return absolutePlaylistEnd - currentTime <= _ranges2['default'].TIME_FUDGE_FACTOR; 28049 } 28050 var bufferedEnd = buffered.end(buffered.length - 1); 28051 28052 // return true if there is too little buffer left and 28053 // buffer has reached absolute end of playlist 28054 return bufferedEnd - currentTime <= _ranges2['default'].TIME_FUDGE_FACTOR && absolutePlaylistEnd - bufferedEnd <= _ranges2['default'].TIME_FUDGE_FACTOR; 28055 } 28056 28057 /** 28058 * Blacklists a playlist when an error occurs for a set amount of time 28059 * making it unavailable for selection by the rendition selection algorithm 28060 * and then forces a new playlist (rendition) selection. 28061 * 28062 * @param {Object=} error an optional error that may include the playlist 28063 * to blacklist 28064 * @param {Number=} blacklistDuration an optional number of seconds to blacklist the 28065 * playlist 28066 */ 28067 }, { 28068 key: 'blacklistCurrentPlaylist', 28069 value: function blacklistCurrentPlaylist(error, blacklistDuration) { 28070 if (error === undefined) error = {}; 28071 28072 var currentPlaylist = undefined; 28073 var nextPlaylist = undefined; 28074 28075 // If the `error` was generated by the playlist loader, it will contain 28076 // the playlist we were trying to load (but failed) and that should be 28077 // blacklisted instead of the currently selected playlist which is likely 28078 // out-of-date in this scenario 28079 currentPlaylist = error.playlist || this.masterPlaylistLoader_.media(); 28080 28081 // If there is no current playlist, then an error occurred while we were 28082 // trying to load the master OR while we were disposing of the tech 28083 if (!currentPlaylist) { 28084 this.error = error; 28085 28086 try { 28087 return this.mediaSource.endOfStream('network'); 28088 } catch (e) { 28089 return this.trigger('error'); 28090 } 28091 } 28092 28093 var isFinalRendition = this.masterPlaylistLoader_.isFinalRendition_(); 28094 28095 if (isFinalRendition) {
vendor: 6,883 bytes, lines 28096-28314
28096 // Never blacklisting this playlist because it's final rendition 28097 _videoJs2['default'].log.warn('Problem encountered with the current ' + 'HLS playlist. Trying again since it is the final playlist.'); 28098 28099 this.tech_.trigger('retryplaylist'); 28100 return this.masterPlaylistLoader_.load(isFinalRendition); 28101 } 28102 // Blacklist this playlist 28103 currentPlaylist.excludeUntil = Date.now() + (blacklistDuration ? blacklistDuration : this.blacklistDuration) * 1000; 28104 this.tech_.trigger('blacklistplaylist'); 28105 this.tech_.trigger({ type: 'usage', name: 'hls-rendition-blacklisted' }); 28106 28107 // Select a new playlist 28108 nextPlaylist = this.selectPlaylist(); 28109 _videoJs2['default'].log.warn('Problem encountered with the current HLS playlist.' + (error.message ? ' ' + error.message : '') + ' Switching to another playlist.'); 28110 28111 return this.masterPlaylistLoader_.media(nextPlaylist); 28112 } 28113 28114 /** 28115 * Pause all segment loaders 28116 */ 28117 }, { 28118 key: 'pauseLoading', 28119 value: function pauseLoading() { 28120 this.mainSegmentLoader_.pause(); 28121 if (this.mediaTypes_.AUDIO.activePlaylistLoader) { 28122 this.audioSegmentLoader_.pause(); 28123 } 28124 if (this.mediaTypes_.SUBTITLES.activePlaylistLoader) { 28125 this.subtitleSegmentLoader_.pause(); 28126 } 28127 } 28128 28129 /** 28130 * set the current time on all segment loaders 28131 * 28132 * @param {TimeRange} currentTime the current time to set 28133 * @return {TimeRange} the current time 28134 */ 28135 }, { 28136 key: 'setCurrentTime', 28137 value: function setCurrentTime(currentTime) { 28138 var buffered = _ranges2['default'].findRange(this.tech_.buffered(), currentTime); 28139 28140 if (!(this.masterPlaylistLoader_ && this.masterPlaylistLoader_.media())) { 28141 // return immediately if the metadata is not ready yet 28142 return 0; 28143 } 28144 28145 // it's clearly an edge-case but don't thrown an error if asked to 28146 // seek within an empty playlist 28147 if (!this.masterPlaylistLoader_.media().segments) { 28148 return 0; 28149 } 28150 28151 // In flash playback, the segment loaders should be reset on every seek, even 28152 // in buffer seeks 28153 var isFlash = this.mode_ === 'flash' || this.mode_ === 'auto' && !_videoJs2['default'].MediaSource.supportsNativeMediaSources(); 28154 28155 // if the seek location is already buffered, continue buffering as 28156 // usual 28157 if (buffered && buffered.length && !isFlash) { 28158 return currentTime; 28159 } 28160 28161 // cancel outstanding requests so we begin buffering at the new 28162 // location 28163 this.mainSegmentLoader_.resetEverything(); 28164 this.mainSegmentLoader_.abort(); 28165 if (this.mediaTypes_.AUDIO.activePlaylistLoader) { 28166 this.audioSegmentLoader_.resetEverything(); 28167 this.audioSegmentLoader_.abort(); 28168 } 28169 if (this.mediaTypes_.SUBTITLES.activePlaylistLoader) { 28170 this.subtitleSegmentLoader_.resetEverything(); 28171 this.subtitleSegmentLoader_.abort(); 28172 } 28173 28174 // start segment loader loading in case they are paused 28175 this.load(); 28176 } 28177 28178 /** 28179 * get the current duration 28180 * 28181 * @return {TimeRange} the duration 28182 */ 28183 }, { 28184 key: 'duration', 28185 value: function duration() { 28186 if (!this.masterPlaylistLoader_) { 28187 return 0; 28188 } 28189 28190 if (this.mediaSource) { 28191 return this.mediaSource.duration; 28192 } 28193 28194 return Hls.Playlist.duration(this.masterPlaylistLoader_.media()); 28195 } 28196 28197 /** 28198 * check the seekable range 28199 * 28200 * @return {TimeRange} the seekable range 28201 */ 28202 }, { 28203 key: 'seekable', 28204 value: function seekable() { 28205 return this.seekable_; 28206 } 28207 }, { 28208 key: 'onSyncInfoUpdate_', 28209 value: function onSyncInfoUpdate_() { 28210 var mainSeekable = undefined; 28211 var audioSeekable = undefined; 28212 28213 if (!this.masterPlaylistLoader_) { 28214 return; 28215 } 28216 28217 var media = this.masterPlaylistLoader_.media(); 28218 28219 if (!media) { 28220 return; 28221 } 28222 28223 var expired = this.syncController_.getExpiredTime(media, this.mediaSource.duration); 28224 28225 if (expired === null) { 28226 // not enough information to update seekable 28227 return; 28228 } 28229 28230 mainSeekable = Hls.Playlist.seekable(media, expired); 28231 28232 if (mainSeekable.length === 0) { 28233 return; 28234 } 28235 28236 if (this.mediaTypes_.AUDIO.activePlaylistLoader) { 28237 media = this.mediaTypes_.AUDIO.activePlaylistLoader.media(); 28238 expired = this.syncController_.getExpiredTime(media, this.mediaSource.duration); 28239 28240 if (expired === null) { 28241 return; 28242 } 28243 28244 audioSeekable = Hls.Playlist.seekable(media, expired); 28245 28246 if (audioSeekable.length === 0) { 28247 return; 28248 } 28249 } 28250 28251 if (!audioSeekable) { 28252 // seekable has been calculated based on buffering video data so it 28253 // can be returned directly 28254 this.seekable_ = mainSeekable; 28255 } else if (audioSeekable.start(0) > mainSeekable.end(0) || mainSeekable.start(0) > audioSeekable.end(0)) { 28256 // seekables are pretty far off, rely on main 28257 this.seekable_ = mainSeekable; 28258 } else { 28259 this.seekable_ = _videoJs2['default'].createTimeRanges([[audioSeekable.start(0) > mainSeekable.start(0) ? audioSeekable.start(0) : mainSeekable.start(0), audioSeekable.end(0) < mainSeekable.end(0) ? audioSeekable.end(0) : mainSeekable.end(0)]]); 28260 } 28261 28262 this.tech_.trigger('seekablechanged'); 28263 } 28264 28265 /** 28266 * Update the player duration 28267 */ 28268 }, { 28269 key: 'updateDuration', 28270 value: function updateDuration() { 28271 var _this4 = this; 28272 28273 var oldDuration = this.mediaSource.duration; 28274 var newDuration = Hls.Playlist.duration(this.masterPlaylistLoader_.media()); 28275 var buffered = this.tech_.buffered(); 28276 var setDuration = function setDuration() { 28277 _this4.mediaSource.duration = newDuration; 28278 _this4.tech_.trigger('durationchange'); 28279 28280 _this4.mediaSource.removeEventListener('sourceopen', setDuration); 28281 }; 28282 28283 if (buffered.length > 0) { 28284 newDuration = Math.max(newDuration, buffered.end(buffered.length - 1)); 28285 } 28286 28287 // if the duration has changed, invalidate the cached value 28288 if (oldDuration !== newDuration) { 28289 // update the duration 28290 if (this.mediaSource.readyState !== 'open') { 28291 this.mediaSource.addEventListener('sourceopen', setDuration); 28292 } else { 28293 setDuration(); 28294 } 28295 } 28296 } 28297 28298 /** 28299 * dispose of the MasterPlaylistController and everything 28300 * that it controls 28301 */ 28302 }, { 28303 key: 'dispose', 28304 value: function dispose() { 28305 var _this5 = this; 28306 28307 this.decrypter_.terminate(); 28308 this.masterPlaylistLoader_.dispose(); 28309 this.mainSegmentLoader_.dispose(); 28310 28311 ['AUDIO', 'SUBTITLES'].forEach(function (type) { 28312 var groups = _this5.mediaTypes_[type].groups; 28313 28314 for (var id in groups) {
28315 groups[id].forEach(function (group) { 28316 if (group.playlistLoader) { 28317 group.playlistLoader.dispose(); 28318 } 28319 }); 28320 } 28321 }); 28322 28323 this.audioSegmentLoader_.dispose(); 28324 this.subtitleSegmentLoader_.dispose(); 28325 } 28326 28327 /** 28328 * return the master playlist object if we have one 28329 * 28330 * @return {Object} the master playlist object that we parsed 28331 */ 28332 }, { 28333 key: 'master', 28334 value: function master() { 28335 return this.masterPlaylistLoader_.master; 28336 } 28337 28338 /** 28339 * return the currently selected playlist 28340 * 28341 * @return {Object} the currently selected playlist object that we parsed 28342 */ 28343 }, { 28344 key: 'media', 28345 value: function media() { 28346 // playlist loader will not return media if it has not been fully loaded 28347 return this.masterPlaylistLoader_.media() || this.initialMedia_; 28348 } 28349 28350 /** 28351 * setup our internal source buffers on our segment Loaders 28352 * 28353 * @private 28354 */ 28355 }, { 28356 key: 'setupSourceBuffers_', 28357 value: function setupSourceBuffers_() { 28358 var media = this.masterPlaylistLoader_.media(); 28359 var mimeTypes = undefined; 28360 28361 // wait until a media playlist is available and the Media Source is 28362 // attached 28363 if (!media || this.mediaSource.readyState !== 'open') { 28364 return; 28365 } 28366 28367 mimeTypes = mimeTypesForPlaylist_(this.masterPlaylistLoader_.master, media); 28368 if (mimeTypes.length < 1) { 28369 this.error = 'No compatible SourceBuffer configuration for the variant stream:' + media.resolvedUri; 28370 return this.mediaSource.endOfStream('decode'); 28371 } 28372 this.mainSegmentLoader_.mimeType(mimeTypes[0]); 28373 if (mimeTypes[1]) { 28374 this.audioSegmentLoader_.mimeType(mimeTypes[1]); 28375 } 28376 28377 // exclude any incompatible variant streams from future playlist 28378 // selection 28379 this.excludeIncompatibleVariants_(media); 28380 } 28381 28382 /** 28383 * Blacklist playlists that are known to be codec or 28384 * stream-incompatible with the SourceBuffer configuration. For 28385 * instance, Media Source Extensions would cause the video element to 28386 * stall waiting for video data if you switched from a variant with 28387 * video and audio to an audio-only one. 28388 * 28389 * @param {Object} media a media playlist compatible with the current 28390 * set of SourceBuffers. Variants in the current master playlist that 28391 * do not appear to have compatible codec or stream configurations 28392 * will be excluded from the default playlist selection algorithm 28393 * indefinitely. 28394 * @private 28395 */ 28396 }, { 28397 key: 'excludeIncompatibleVariants_', 28398 value: function excludeIncompatibleVariants_(media) { 28399 var master = this.masterPlaylistLoader_.master; 28400 var codecCount = 2; 28401 var videoCodec = null; 28402 var codecs = undefined; 28403 28404 if (media.attributes.CODECS) { 28405 codecs = (0, _utilCodecsJs.parseCodecs)(media.attributes.CODECS); 28406 videoCodec = codecs.videoCodec; 28407 codecCount = codecs.codecCount; 28408 }
28409 master.playlists.forEach(function (variant) { 28410 var variantCodecs = { 28411 codecCount: 2, 28412 videoCodec: null 28413 }; 28414 28415 if (variant.attributes.CODECS) { 28416 var codecString = variant.attributes.CODECS; 28417 28418 variantCodecs = (0, _utilCodecsJs.parseCodecs)(codecString); 28419 28420 if (window.MediaSource && window.MediaSource.isTypeSupported && !window.MediaSource.isTypeSupported('video/mp4; codecs="' + mapLegacyAvcCodecs_(codecString) + '"')) { 28421 variant.excludeUntil = Infinity; 28422 } 28423 } 28424 28425 // if the streams differ in the presence or absence of audio or 28426 // video, they are incompatible 28427 if (variantCodecs.codecCount !== codecCount) { 28428 variant.excludeUntil = Infinity; 28429 } 28430 28431 // if h.264 is specified on the current playlist, some flavor of 28432 // it must be specified on all compatible variants 28433 if (variantCodecs.videoCodec !== videoCodec) { 28434 variant.excludeUntil = Infinity; 28435 } 28436 }); 28437 } 28438 }, { 28439 key: 'updateAdCues_', 28440 value: function updateAdCues_(media) { 28441 var offset = 0; 28442 var seekable = this.seekable(); 28443 28444 if (seekable.length) { 28445 offset = seekable.start(0); 28446 } 28447 28448 _adCueTags2['default'].updateAdCues(media, this.cueTagsTrack_, offset); 28449 } 28450 28451 /** 28452 * Calculates the desired forward buffer length based on current time 28453 * 28454 * @return {Number} Desired forward buffer length in seconds 28455 */ 28456 }, { 28457 key: 'goalBufferLength', 28458 value: function goalBufferLength() { 28459 var currentTime = this.tech_.currentTime(); 28460 var initial = _config2['default'].GOAL_BUFFER_LENGTH; 28461 var rate = _config2['default'].GOAL_BUFFER_LENGTH_RATE; 28462 var max = Math.max(initial, _config2['default'].MAX_GOAL_BUFFER_LENGTH); 28463 28464 return Math.min(initial + currentTime * rate, max); 28465 } 28466 28467 /** 28468 * Calculates the desired buffer low water line based on current time 28469 * 28470 * @return {Number} Desired buffer low water line in seconds 28471 */ 28472 }, { 28473 key: 'bufferLowWaterLine', 28474 value: function bufferLowWaterLine() { 28475 var currentTime = this.tech_.currentTime(); 28476 var initial = _config2['default'].BUFFER_LOW_WATER_LINE; 28477 var rate = _config2['default'].BUFFER_LOW_WATER_LINE_RATE; 28478 var max = Math.max(initial, _config2['default'].MAX_BUFFER_LOW_WATER_LINE); 28479 28480 return Math.min(initial + currentTime * rate, max); 28481 } 28482 }]); 28483 28484 return MasterPlaylistController; 28485})(_videoJs2['default'].EventTarget); 28486 28487exports.MasterPlaylistController = MasterPlaylistController; 28488}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 28489},{"./ad-cue-tags":1,"./config":3,"./decrypter-worker":4,"./media-groups":6,"./playlist-loader":9,"./ranges":12,"./segment-loader":16,"./sync-controller":18,"./util/codecs.js":19,"./vtt-segment-loader":20,"videojs-contrib-media-sources/es5/codec-utils":65,"webworkify":76}],6:[function(require,module,exports){ 28490(function (global){ 28491'use strict'; 28492 28493Object.defineProperty(exports, '__esModule', { 28494 value: true 28495}); 28496 28497function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 28498 28499var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null); 28500 28501var _videoJs2 = _interopRequireDefault(_videoJs); 28502 28503var _playlistLoader = require('./playlist-loader'); 28504 28505var _playlistLoader2 = _interopRequireDefault(_playlistLoader); 28506 28507var noop = function noop() {}; 28508 28509/** 28510 * Convert the properties of an HLS track into an audioTrackKind. 28511 * 28512 * @private 28513 */ 28514var audioTrackKind_ = function audioTrackKind_(properties) { 28515 var kind = properties['default'] ? 'main' : 'alternative'; 28516 28517 if (properties.characteristics && properties.characteristics.indexOf('public.accessibility.describes-video') >= 0) { 28518 kind = 'main-desc'; 28519 } 28520 28521 return kind; 28522}; 28523 28524/** 28525 * Pause provided segment loader and playlist loader if active 28526 * 28527 * @param {SegmentLoader} segmentLoader 28528 * SegmentLoader to pause 28529 * @param {Object} mediaType 28530 * Active media type 28531 * @function stopLoaders 28532 */ 28533var stopLoaders = function stopLoaders(segmentLoader, mediaType) { 28534 segmentLoader.abort(); 28535 segmentLoader.pause(); 28536 28537 if (mediaType && mediaType.activePlaylistLoader) { 28538 mediaType.activePlaylistLoader.pause(); 28539 mediaType.activePlaylistLoader = null; 28540 } 28541}; 28542 28543exports.stopLoaders = stopLoaders; 28544/** 28545 * Start loading provided segment loader and playlist loader 28546 * 28547 * @param {PlaylistLoader} playlistLoader 28548 * PlaylistLoader to start loading 28549 * @param {Object} mediaType 28550 * Active media type 28551 * @function startLoaders 28552 */ 28553var startLoaders = function startLoaders(playlistLoader, mediaType) { 28554 // Segment loader will be started after `loadedmetadata` or `loadedplaylist` from the 28555 // playlist loader 28556 mediaType.activePlaylistLoader = playlistLoader; 28557 playlistLoader.load(); 28558}; 28559 28560exports.startLoaders = startLoaders; 28561/** 28562 * Returns a function to be called when the media group changes. It performs a 28563 * non-destructive (preserve the buffer) resync of the SegmentLoader. This is because a 28564 * change of group is merely a rendition switch of the same content at another encoding, 28565 * rather than a change of content, such as switching audio from English to Spanish. 28566 * 28567 * @param {String} type 28568 * MediaGroup type 28569 * @param {Object} settings 28570 * Object containing required information for media groups 28571 * @return {Function} 28572 * Handler for a non-destructive resync of SegmentLoader when the active media 28573 * group changes. 28574 * @function onGroupChanged 28575 */ 28576var onGroupChanged = function onGroupChanged(type, settings) { 28577 return function () { 28578 var _settings$segmentLoaders = settings.segmentLoaders; 28579 var segmentLoader = _settings$segmentLoaders[type]; 28580 var mainSegmentLoader = _settings$segmentLoaders.main; 28581 var mediaType = settings.mediaTypes[type]; 28582 28583 var activeTrack = mediaType.activeTrack(); 28584 var activeGroup = mediaType.activeGroup(activeTrack); 28585 var previousActiveLoader = mediaType.activePlaylistLoader; 28586 28587 stopLoaders(segmentLoader, mediaType); 28588 28589 if (!activeGroup) { 28590 // there is no group active 28591 return; 28592 } 28593 28594 if (!activeGroup.playlistLoader) { 28595 if (previousActiveLoader) { 28596 // The previous group had a playlist loader but the new active group does not 28597 // this means we are switching from demuxed to muxed audio. In this case we want to 28598 // do a destructive reset of the main segment loader and n
28598ot restart the audio 28599 // loaders. 28600 mainSegmentLoader.resetEverything(); 28601 } 28602 return; 28603 } 28604 28605 // Non-destructive resync 28606 segmentLoader.resyncLoader(); 28607 28608 startLoaders(activeGroup.playlistLoader, mediaType); 28609 }; 28610}; 28611 28612exports.onGroupChanged = onGroupChanged; 28613/** 28614 * Returns a function to be called when the media track changes. It performs a 28615 * destructive reset of the SegmentLoader to ensure we start loading as close to 28616 * currentTime as possible. 28617 * 28618 * @param {String} type 28619 * MediaGroup type 28620 * @param {Object} settings 28621 * Object containing required information for media groups 28622 * @return {Function} 28623 * Handler for a destructive reset of SegmentLoader when the active media 28624 * track changes. 28625 * @function onTrackChanged 28626 */ 28627var onTrackChanged = function onTrackChanged(type, settings) { 28628 return function () { 28629 var _settings$segmentLoaders2 = settings.segmentLoaders; 28630 var segmentLoader = _settings$segmentLoaders2[type]; 28631 var mainSegmentLoader = _settings$segmentLoaders2.main; 28632 var mediaType = settings.mediaTypes[type]; 28633 28634 var activeTrack = mediaType.activeTrack(); 28635 var activeGroup = mediaType.activeGroup(activeTrack); 28636 var previousActiveLoader = mediaType.activePlaylistLoader; 28637 28638 stopLoaders(segmentLoader, mediaType); 28639 28640 if (!activeGroup) { 28641 // there is no group active so we do not want to restart loaders 28642 return; 28643 } 28644 28645 if (!activeGroup.playlistLoader) { 28646 // when switching from demuxed audio/video to muxed audio/video (noted by no playlist 28647 // loader for the audio group), we want to do a destructive reset of the main segment 28648 // loader and not restart the audio loaders 28649 mainSegmentLoader.resetEverything(); 28650 return; 28651 } 28652 28653 if (previousActiveLoader === activeGroup.playlistLoader) { 28654 // Nothing has actually changed. This can happen because track change events can fire 28655 // multiple times for a "single" change. One for enabling the new active track, and 28656 // one for disabling the track that was active 28657 startLoaders(activeGroup.playlistLoader, mediaType); 28658 return; 28659 } 28660 28661 if (segmentLoader.track) { 28662 // For WebVTT, set the new text track in the segmentloader 28663 segmentLoader.track(activeTrack); 28664 } 28665 28666 // destructive reset 28667 segmentLoader.resetEverything(); 28668 28669 startLoaders(activeGroup.playlistLoader, mediaType); 28670 }; 28671}; 28672 28673exports.onTrackChanged = onTrackChanged; 28674var onError = { 28675 /** 28676 * Returns a function to be called when a SegmentLoader or PlaylistLoader encounters 28677 * an error. 28678 * 28679 * @param {String} type 28680 * MediaGroup type 28681 * @param {Object} settings 28682 * Object containing required information for media groups 28683 * @return {Function} 28684 * Error handler. Logs warning (or error if the playlist is blacklisted) to 28685 * console and switches back to default audio track. 28686 * @function onError.AUDIO 28687 */ 28688 AUDIO: function AUDIO(type, settings) { 28689 return function () { 28690 var segmentLoader = settings.segmentLoaders[type]; 28691 var mediaType = settings.mediaTypes[type]; 28692 var blacklistCurrentPlaylist = settings.blacklistCurrentPlaylist; 28693 28694 stopLoaders(segmentLoader, mediaType); 28695 28696 // switch back to default audio track 28697 var activeTrack = mediaType.activeTrack(); 28698 var activeGroup = mediaType.activeGroup(); 28699 var id = (activeGroup.filter(function (group) { 28700 return group['default']; 28701 })[0] || activeGroup[0]).id; 28702 var defaultTrack = mediaType.tracks[id]; 28703 28704 if (activeTrack === defaultTrack) { 28705 // Default track encountered an error. All we can do now is blacklist the current 28706 // rendition and hope another will switch audio groups 28707 blacklistCurrentPlaylist({ 28708 message: 'Problem encountered loading the default audio track.' 28709 }); 28710 return; 28711 } 28712 28713 _videoJs2['default'].log.warn('Problem encountered loading the alternate audio track.' + 'Switching back to default.'); 28714 28715 for (var trackId in mediaType.tracks) { 28716 mediaType.tracks[trackId].enabled = mediaType.tracks[trackId] === defaultTrack; 28717 } 28718 28719 mediaType.onTrackChanged(); 28720 }; 28721 }, 28722 /** 28723 * Returns a function to be called when a SegmentLoader or PlaylistLoader encounters 28724 * an error. 28725 * 28726 * @param {String} type 28727 * MediaGroup type 28728 * @param {Object} settings 28729 * Object containing required information for media groups 28730 * @return {Function} 28731 * Error handler. Logs warning to console and disables the active subtitle track 28732 * @function onError.SUBTITLES 28733 */ 28734 SUBTITLES: function SUBTITLES(type, settings) { 28735 return function () { 28736 var segmentLoader = settings.segmentLoaders[type]; 28737 var mediaType = settings.mediaTypes[type]; 28738 28739 _videoJs2['default'].log.warn('Problem encountered loading the subtitle track.' + 'Disabling subtitle track.'); 28740 28741 stopLoaders(segmentLoader, mediaType); 28742 28743 var track = mediaType.activeTrack(); 28744 28745 if (track) { 28746 track.mode = 'disabled'; 28747 } 28748 28749 mediaType.onTrackChanged(); 28750 }; 28751 } 28752}; 28753 28754exports.onError = onError; 28755var setupListeners = { 28756 /** 28757 * Setup event listeners for audio playlist loader 28758 * 28759 * @param {String} type 28760 * MediaGroup type 28761 * @param {PlaylistLoader|null} playlistLoader 28762 * PlaylistLoader to register listeners on 28763 * @param {Object} settings 28764 * Object containing required information for media groups 28765 * @function setupListeners.AUDIO 28766 */ 28767 AUDIO: function AUDIO(type, playlistLoader, settings) { 28768 if (!playlistLoader) { 28769 // no playlist loader means audio will be muxed with the video 28770 return; 28771 } 28772 28773 var tech = settings.tech; 28774 var requestOptions = settings.requestOptions; 28775 var segmentLoader = settings.segmentLoaders[type]; 28776 28777 playlistLoader.on('loadedmetadata', function () { 28778 var media = playlistLoader.media(); 28779 28780 segmentLoader.playlist(media, requestOptions); 28781 28782 // if the video is already playing, or if this isn't a live video and preload 28783 // permits, start downloading segments 28784 if (!tech.paused() || media.endList && tech.preload() !== 'none') { 28785 segmentLoader.load(); 28786 } 28787 }); 28788 28789 playlistLoader.on('loadedplaylist', function () { 28790 segmentLoader.playlist(playlistLoader.media(), requestOptions); 28791 28792 // If the player isn't paused, ensure that the segment loader is running 28793 if (!tech.paused()) { 28794 segmentLoader.load(); 28795 } 28796 }); 28797 28798 playlistLoader.on('error', onError[type](type, settings)); 28799 }, 28800 /** 28801 * Setup event listeners for subtitle playlist loader 28802 * 28803 * @param {String} type 28804 * MediaGroup type 28805 * @param {PlaylistLoader|null} playlistLoader 28806 * PlaylistLoader to register listeners on 28807 * @param {Object} settings 28808 * Object containing required information for media groups 28809 * @function setupListeners.SUBTITLES 28810 */ 28811 SUBTITLES: function SUBTITLES(type, playlistLoader, settings) { 28812 var tech = settings.tech; 28813 var requestOptions = settings.requestOptions; 28814 var segmentLoader = settings.segmentLoaders[type]; 28815 var mediaType = settings.mediaTypes[type]; 28816 28817 playlistLoader.on('loadedmetadata', function () { 28818 var media = playlistLoader.media(); 28819 28820 segmentLoader.playlist(media, requestOptions); 28821 segmentLoader.track(mediaType.activeTrack()); 28822 28823 // if the video is already playing, or if this isn't a live video and preload 28824 // permits, start downloading segments 28825 if (!tech.paused() || media.endList && tech.preload() !== 'none') { 28826 segmentLoader.load(); 28827 } 28828 }); 28829 28830 playlistLoader.on('loadedplaylist', function () { 28831 segmentLoader.playlist(playlistLoader.media(), requestOptions); 28832 28833 // If the player isn't paused, ensure that the segment loader is running 28834 if (!tech.paused()) { 28835 segmentLoader.load(); 28836 } 28837 }); 28838 28839 playlistLoader.on('error', onError[type](type, settings)); 28840 } 28841}; 28842 28843exports.setupListeners = setupListeners; 28844var initialize = { 28845 /** 28846 * Setup PlaylistLoaders and AudioTracks for the audio groups 28847 * 28848 * @param {String} type 28849 * MediaGroup type 28850 * @param {Object} settings 28851 * Object containing required information for media groups 28852 * @function initialize.AUDIO 28853 */ 28854 'AUDIO': function AUDIO(type, settings) { 28855 var mode = settings.mode; 28856 var hls = settings.hls; 28857 var segmentLoader = settings.segmentLoaders[type]; 28858 var withCredentials = settings.requestOptions.withCredentials; 28859 var mediaGroups = settings.master.mediaGroups; 28860 var _settings$mediaTypes$type = settings.mediaTypes[type]; 28861 var groups = _settings$mediaTypes$type.groups; 28862 var tracks = _settings$mediaTypes$type.tracks; 28863 28864 // force a default if we have none or we are not
28865 // in html5 mode (the only mode to support more than one 28866 // audio track) 28867 if (!mediaGroups[type] || Object.keys(mediaGroups[type]).length === 0 || mode !== 'html5') { 28868 mediaGroups[type] = { main: { 'default': { 'default': true } } }; 28869 } 28870 28871 for (var groupId in mediaGroups[type]) { 28872 if (!groups[groupId]) { 28873 groups[groupId] = []; 28874 } 28875 28876 for (var variantLabel in mediaGroups[type][groupId]) { 28877 var properties = mediaGroups[type][groupId][variantLabel]; 28878 var playlistLoader = undefined; 28879 28880 if (properties.resolvedUri) { 28881 playlistLoader = new _playlistLoader2['default'](properties.resolvedUri, hls, withCredentials); 28882 } else { 28883 // no resolvedUri means the audio is muxed with the video when using this 28884 // audio track 28885 playlistLoader = null; 28886 } 28887 28888 properties = _videoJs2['default'].mergeOptions({ id: variantLabel, playlistLoader: playlistLoader }, properties); 28889 28890 setupListeners[type](type, properties.playlistLoader, settings); 28891 28892 groups[groupId].push(properties); 28893 28894 if (typeof tracks[variantLabel] === 'undefined') { 28895 var track = new _videoJs2['default'].AudioTrack({ 28896 id: variantLabel, 28897 kind: audioTrackKind_(properties), 28898 enabled: false, 28899 language: properties.language, 28900 'default': properties['default'], 28901 label: variantLabel 28902 }); 28903 28904 tracks[variantLabel] = track; 28905 } 28906 } 28907 } 28908 28909 // setup single error event handler for the segment loader 28910 segmentLoader.on('error', onError[type](type, settings)); 28911 }, 28912 /** 28913 * Setup PlaylistLoaders and TextTracks for the subtitle groups 28914 * 28915 * @param {String} type 28916 * MediaGroup type 28917 * @param {Object} settings 28918 * Object containing required information for media groups 28919 * @function initialize.SUBTITLES 28920 */ 28921 'SUBTITLES': function SUBTITLES(type, settings) { 28922 var tech = settings.tech; 28923 var hls = settings.hls; 28924 var segmentLoader = settings.segmentLoaders[type]; 28925 var withCredentials = settings.requestOptions.withCredentials; 28926 var mediaGroups = settings.master.mediaGroups; 28927 var _settings$mediaTypes$type2 = settings.mediaTypes[type]; 28928 var groups = _settings$mediaTypes$type2.groups; 28929 var tracks = _settings$mediaTypes$type2.tracks; 28930 28931 for (var groupId in mediaGroups[type]) { 28932 if (!groups[groupId]) { 28933 groups[groupId] = []; 28934 } 28935 28936 for (var variantLabel in mediaGroups[type][groupId]) { 28937 if (mediaGroups[type][groupId][variantLabel].forced) { 28938 // Subtitle playlists with the forced attribute are not selectable in Safari. 28939 // According to Apple's HLS Authoring Specification: 28940 // If content has forced subtitles and regular subtitles in a given language, 28941 // the regular subtitles track in that language MUST contain both the forced 28942 // subtitles and the regular subtitles for that language. 28943 // Because of this requirement and that Safari does not add forced subtitles, 28944 // forced subtitles are skipped here to maintain consistent experience across 28945 // all platforms 28946 continue; 28947 } 28948 28949 var properties = mediaGroups[type][groupId][variantLabel]; 28950 28951 properties = _videoJs2['default'].mergeOptions({ 28952 id: variantLabel, 28953 playlistLoader: new _playlistLoader2['default'](properties.resolvedUri, hls, withCredentials) 28954 }, properties); 28955 28956 setupListeners[type](type, properties.playlistLoader, settings); 28957 28958 groups[groupId].push(properties); 28959 28960 if (typeof tracks[variantLabel] === 'undefined') { 28961 var track = tech.addRemoteTextTrack({ 28962 id: variantLabel, 28963 kind: 'subtitles', 28964 enabled: false, 28965 language: properties.language, 28966 label: variantLabel 28967 }, false).track; 28968 28969 tracks[variantLabel] = track; 28970 } 28971 } 28972 } 28973 28974 // setup single error event handler for the segment loader 28975 segmentLoader.on('error', onError[type](type, settings)); 28976 }, 28977 /** 28978 * Setup TextTracks for the closed-caption groups 28979 * 28980 * @param {String} type 28981 * MediaGroup type 28982 * @param {Object} settings 28983 * Object containing required information for media groups 28984 * @function initialize['CLOSED-CAPTIONS'] 28985 */ 28986 'CLOSED-CAPTIONS': function CLOSEDCAPTIONS(type, settings) { 28987 var tech = settings.tech; 28988 var mediaGroups = settings.master.mediaGroups; 28989 var _settings$mediaTypes$type3 = settings.mediaTypes[type]; 28990 var groups = _settings$mediaTypes$type3.groups; 28991 var tracks = _settings$mediaTypes$type3.tracks; 28992 28993 for (var groupId in mediaGroups[type]) { 28994 if (!groups[groupId]) { 28995 groups[groupId] = []; 28996 } 28997 28998 for (var variantLabel in mediaGroups[type][groupId]) { 28999 var properties = mediaGroups[type][groupId][variantLabel]; 29000 29001 // We only support CEA608 captions for now, so ignore anything that 29002 // doesn't use a CCx INSTREAM-ID 29003 if (!properties.instreamId.match(/CC\d/)) { 29004 continue; 29005 } 29006 29007 // No PlaylistLoader is required for Closed-Captions because the captions are 29008 // embedded within the video stream 29009 groups[groupId].push(_videoJs2['default'].mergeOptions({ id: variantLabel }, properties)); 29010 29011 if (typeof tracks[variantLabel] === 'undefined') { 29012 var track = tech.addRemoteTextTrack({ 29013 id: properties.instreamId, 29014 kind: 'captions', 29015 enabled: false, 29016 language: properties.language, 29017 label: variantLabel 29018 }, false).track; 29019 29020 tracks[variantLabel] = track; 29021 } 29022 } 29023 } 29024 } 29025}; 29026 29027exports.initialize = initialize; 29028/** 29029 * Returns a function used to get the active group of the provided type 29030 * 29031 * @param {String} type 29032 * MediaGroup type 29033 * @param {Object} settings 29034 * Object containing required information for media groups 29035 * @return {Function} 29036 * Function that returns the active media group for the provided type. Takes an 29037 * optional parameter {TextTrack} track. If no track is provided, a list of all 29038 * variants in the group, otherwise the variant corresponding to the provided 29039 * track is returned. 29040 * @function activeGroup 29041 */ 29042var activeGroup = function activeGroup(type, settings) { 29043 return function (track) { 29044 var masterPlaylistLoader = settings.masterPlaylistLoader; 29045 var groups = settings.mediaTypes[type].groups; 29046 29047 var media = masterPlaylistLoader.media(); 29048 29049 if (!media) { 29050 return null; 29051 } 29052 29053 var variants = null; 29054 29055 if (media.attributes[type]) { 29056 variants = groups[media.attributes[type]]; 29057 } 29058 29059 variants = variants || groups.main; 29060 29061 if (typeof track === 'undefined') { 29062 return variants; 29063 } 29064 29065 if (track === null) { 29066 // An active track was specified so a corresponding group is expected. track === null 29067 // means no track is currently active so there is no corresponding group 29068 return null; 29069 } 29070 29071 return variants.filter(function (props) { 29072 return props.id === track.id; 29073 })[0] || null; 29074 }; 29075}; 29076 29077exports.activeGroup = activeGroup; 29078var activeTrack = { 29079 /** 29080 * Returns a function used to get the active track of type provided 29081 * 29082 * @param {String} type 29083 * MediaGroup type 29084 * @param {Object} settings 29085 * Object containing required information for media groups 29086 * @return {Function} 29087 * Function that returns the active media track for the provided type. Returns 29088 * null if no track is active 29089 * @function activeTrack.AUDIO 29090 */ 29091 AUDIO: function AUDIO(type, settings) { 29092 return function () { 29093 var tracks = settings.mediaTypes[type].tracks; 29094 29095 for (var id in tracks) { 29096 if (tracks[id].enabled) { 29097 return tracks[id]; 29098 } 29099 } 29100 29101 return null; 29102 }; 29103 }, 29104 /** 29105 * Returns a function used to get the active track of type provided 29106 * 29107 * @param {String} type 29108 * MediaGroup type 29109 * @param {Object} settings 29110 * Object containing required information for media groups 29111 * @return {Function} 29112 * Function that returns the active media track for the provided type. Returns 29113 * null if no track is active 29114 * @function activeTrack.SUBTITLES 29115 */ 29116 SUBTITLES: function SUBTITLES(type, settings) { 29117 return function () { 29118 var tracks = settings.mediaTypes[type].tracks; 29119 29120 for (var id in tracks) { 29121 if (tracks[id].mode === 'showing') { 29122 return tracks[id]; 29123 } 29124 } 29125 29126 return null; 29127 }; 29128 } 29129}; 29130 29131exports.activeTrack = activeTrack; 29132/** 29133 * Setup PlaylistLoaders and Tracks for media groups (Audio, Subtitles, 29134 * Closed-Captions) specified in the master manifest. 29135 * 29136 * @param {Object} settings 29137 * Object containing required information for setting up the media groups 29138 * @param {SegmentLoader} settings.segmentLoaders.AUDIO 29139 * Audio segment loader 29140 * @param {SegmentLoader} settings.segmentLoaders.SUBTITLES 29141 * Subtitle segment loader 29142 * @param {SegmentLoader} settings.segmentLoaders.main 29143 * Main segment loader 29144 * @param {Tech} settings.tech 29145 * The tech of the player 29146 * @param {Object} settings.requestOptions 29147 * XHR request options used by the segment loaders 29148 * @param {PlaylistLoader} settings.masterPlaylistLoader 29149 * PlaylistLoader for the master source 29150 * @param {String} mode 29151 * Mode of the hls source handler. Can be 'auto', 'html5', or 'flash' 29152 * @param {HlsHandler} settings.hls 29153 * HLS SourceHandler 29154 * @param {Object} settings.master 29155 * The parsed master manifest 29156 * @param {Object} settings.mediaTypes 29157 * Object to store the loaders, tracks, and utility methods for each media type 29158 * @param {Function} settings.blacklistCurrentPlaylist 29159 * Blacklists the current rendition and forces a rendition switch. 29160 * @function setupMediaGroups 29161 */ 29162var setupMediaGroups = function setupMediaGroups(settings) { 29163 ['AUDIO', 'SUBTITLES', 'CLOSED-CAPTIONS'].forEach(function (type) { 29164 initialize[type](type, settings); 29165 }); 29166 29167 var mediaTypes = settings.mediaTypes; 29168 var masterPlaylistLoader = settings.masterPlaylistLoader; 29169 var tech = settings.tech; 29170 var hls = settings.hls; 29171 29172 // setup active group and track getters and change event handlers 29173 ['AUDIO', 'SUBTITLES'].forEach(function (type) { 29174 mediaTypes[type].activeGroup = activeGroup(type, settings); 29175 mediaTypes[type].activeTrack = activeTrack[type](type, settings); 29176 mediaTypes[type].onGroupChanged = onGroupChanged(type, settings); 29177 mediaTypes[type].onTrackChanged = onTrackChanged(type, settings); 29178 }); 29179 29180 // DO NOT enable the default subtitle or caption track. 29181 // DO enable the default audio track 29182 var audioGroup = mediaTypes.AUDIO.activeGroup(); 29183 var groupId = (audioGroup.filter(function (group) { 29184 return group['default']; 29185 })[0] || audioGroup[0]).id; 29186 29187 mediaTypes.AUDIO.tracks[groupId].enabled = true; 29188 mediaTypes.AUDIO.onTrackChanged(); 29189 29190 masterPlaylistLoader.on('mediachange', function () { 29191 ['AUDIO', 'SUBTITLES'].forEach(function (type) { 29192 return mediaTypes[type].onGroupChanged(); 29193 }); 29194 }); 29195 29196 // custom audio track change event handler for usage event 29197 var onAudioTrackChanged = function onAudioTrackChanged() { 29198 mediaTypes.AUDIO.onTrackChanged(); 29199 tech.trigger({ type: 'usage', name: 'hls-audio-change' }); 29200 }; 29201 29202 tech.audioTracks().addEventListener('change', onAudioTrackChanged); 29203 tech.remoteTextTracks().addEventListener('change', mediaTypes.SUBTITLES.onTrackChanged); 29204 29205 hls.on('dispose', function () { 29206 tech.audioTracks().removeEventListener('change', onAudioTrackChanged); 29207 tech.remoteTextTracks().removeEventListener('change', mediaTypes.SUBTITLES.onTrackChanged); 29208 }); 29209 29210 // clear existing audio tracks and add the ones we just created 29211 tech.clearTracks('audio'); 29212 29213 for (var id in mediaTypes.AUDIO.tracks) { 29214 tech.audioTracks().addTrack(mediaTypes.AUDIO.tracks[id]); 29215 } 29216}; 29217 29218exports.setupMediaGroups = setupMediaGroups; 29219/** 29220 * Creates skeleton object used to store the loaders, tracks, and utility methods for each 29221 * media type 29222 * 29223 * @return {Object} 29224 * Object to store the loaders, tracks, and utility methods for each media type 29225 * @function createMediaTypes 29226 */ 29227var createMediaTypes = function createMediaTypes() { 29228 var mediaTypes = {}; 29229 29230 ['AUDIO', 'SUBTITLES', 'CLOSED-CAPTIONS'].forEach(function (type) { 29231 mediaTypes[type] = { 29232 groups: {}, 29233 tracks: {}, 29234 activePlaylistLoader: null, 29235 activeGroup: noop, 29236 activeTrack: noop, 29237 onGroupChanged: noop, 29238 onTrackChanged: noop 29239 }; 29240 }); 29241 29242 return mediaTypes; 29243}; 29244exports.createMediaTypes = createMediaTypes; 29245}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 29246},{"./playlist-loader":9}],7:[function(require,module,exports){ 29247(function (global){ 29248'use strict'; 29249 29250Object.defineProperty(exports, '__esModule', { 29251 value: true 29252}); 29253 29254function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 29255 29256var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null); 29257 29258var _videoJs2 = _interopRequireDefault(_videoJs); 29259 29260var _binUtils = require('./bin-utils'); 29261 29262var REQUEST_ERRORS = { 29263 FAILURE: 2, 29264 TIMEOUT: -101, 29265 ABORTED: -102 29266}; 29267 29268exports.REQUEST_ERRORS = REQUEST_ERRORS; 29269/** 29270 * Turns segment byterange into a string suitable for use in 29271 * HTTP Range requests 29272 * 29273 * @param {Object} byterange - an object with two values defining the start and end 29274 * of a byte-range 29275 */ 29276var byterangeStr = function byterangeStr(byterange) { 29277 var byterangeStart = undefined; 29278 var byterangeEnd = undefined; 29279 29280 // `byterangeEnd` is one less than `offset + length` because the HTTP range 29281 // header uses inclusive ranges 29282 byterangeEnd = byterange.offset + byterange.length - 1; 29283 byterangeStart = byterange.offset; 29284 return 'bytes=' + byterangeStart + '-' + byterangeEnd; 29285}; 29286 29287/** 29288 * Defines headers for use in the xhr request for a particular segment. 29289 * 29290 * @param {Object} segment - a simplified copy of the segmentInfo object 29291 * from SegmentLoader 29292 */ 29293var segmentXhrHeaders = function segmentXhrHeaders(segment) { 29294 var headers = {}; 29295 29296 if (segment.byterange) { 29297 headers.Range = byterangeStr(segment.byterange); 29298 } 29299 return headers; 29300}; 29301 29302/** 29303 * Abort all requests 29304 * 29305 * @param {Object} activeXhrs - an object that tracks all XHR requests 29306 */ 29307var abortAll = function abortAll(activeXhrs) {
vendor: 4,369 bytes, lines 29308-29455
29308 activeXhrs.forEach(function (xhr) { 29309 xhr.abort(); 29310 }); 29311}; 29312 29313/** 29314 * Gather important bandwidth stats once a request has completed 29315 * 29316 * @param {Object} request - the XHR request from which to gather stats 29317 */ 29318var getRequestStats = function getRequestStats(request) { 29319 return { 29320 bandwidth: request.bandwidth, 29321 bytesReceived: request.bytesReceived || 0, 29322 roundTripTime: request.roundTripTime || 0 29323 }; 29324}; 29325 29326/** 29327 * If possible gather bandwidth stats as a request is in 29328 * progress 29329 * 29330 * @param {Event} progressEvent - an event object from an XHR's progress event 29331 */ 29332var getProgressStats = function getProgressStats(progressEvent) { 29333 var request = progressEvent.target; 29334 var roundTripTime = Date.now() - request.requestTime; 29335 var stats = { 29336 bandwidth: Infinity, 29337 bytesReceived: 0, 29338 roundTripTime: roundTripTime || 0 29339 }; 29340 29341 stats.bytesReceived = progressEvent.loaded; 29342 // This can result in Infinity if stats.roundTripTime is 0 but that is ok 29343 // because we should only use bandwidth stats on progress to determine when 29344 // abort a request early due to insufficient bandwidth 29345 stats.bandwidth = Math.floor(stats.bytesReceived / stats.roundTripTime * 8 * 1000); 29346 29347 return stats; 29348}; 29349 29350/** 29351 * Handle all error conditions in one place and return an object 29352 * with all the information 29353 * 29354 * @param {Error|null} error - if non-null signals an error occured with the XHR 29355 * @param {Object} request - the XHR request that possibly generated the error 29356 */ 29357var handleErrors = function handleErrors(error, request) { 29358 if (request.timedout) { 29359 return { 29360 status: request.status, 29361 message: 'HLS request timed-out at URL: ' + request.uri, 29362 code: REQUEST_ERRORS.TIMEOUT, 29363 xhr: request 29364 }; 29365 } 29366 29367 if (request.aborted) { 29368 return { 29369 status: request.status, 29370 message: 'HLS request aborted at URL: ' + request.uri, 29371 code: REQUEST_ERRORS.ABORTED, 29372 xhr: request 29373 }; 29374 } 29375 29376 if (error) { 29377 return { 29378 status: request.status, 29379 message: 'HLS request errored at URL: ' + request.uri, 29380 code: REQUEST_ERRORS.FAILURE, 29381 xhr: request 29382 }; 29383 } 29384 29385 return null; 29386}; 29387 29388/** 29389 * Handle responses for key data and convert the key data to the correct format 29390 * for the decryption step later 29391 * 29392 * @param {Object} segment - a simplified copy of the segmentInfo object 29393 * from SegmentLoader 29394 * @param {Function} finishProcessingFn - a callback to execute to continue processing 29395 * this request 29396 */ 29397var handleKeyResponse = function handleKeyResponse(segment, finishProcessingFn) { 29398 return function (error, request) { 29399 var response = request.response; 29400 var errorObj = handleErrors(error, request); 29401 29402 if (errorObj) { 29403 return finishProcessingFn(errorObj, segment); 29404 } 29405 29406 if (response.byteLength !== 16) { 29407 return finishProcessingFn({ 29408 status: request.status, 29409 message: 'Invalid HLS key at URL: ' + request.uri, 29410 code: REQUEST_ERRORS.FAILURE, 29411 xhr: request 29412 }, segment); 29413 } 29414 29415 var view = new DataView(response); 29416 29417 segment.key.bytes = new Uint32Array([view.getUint32(0), view.getUint32(4), view.getUint32(8), view.getUint32(12)]); 29418 return finishProcessingFn(null, segment); 29419 }; 29420}; 29421 29422/** 29423 * Handle init-segment responses 29424 * 29425 * @param {Object} segment - a simplified copy of the segmentInfo object 29426 * from SegmentLoader 29427 * @param {Function} finishProcessingFn - a callback to execute to continue processing 29428 * this request 29429 */ 29430var handleInitSegmentResponse = function handleInitSegmentResponse(segment, finishProcessingFn) { 29431 return function (error, request) { 29432 var response = request.response; 29433 var errorObj = handleErrors(error, request); 29434 29435 if (errorObj) { 29436 return finishProcessingFn(errorObj, segment); 29437 } 29438 29439 // stop processing if received empty content 29440 if (response.byteLength === 0) { 29441 return finishProcessingFn({ 29442 status: request.status, 29443 message: 'Empty HLS segment content at URL: ' + request.uri, 29444 code: REQUEST_ERRORS.FAILURE, 29445 xhr: request 29446 }, segment); 29447 } 29448 29449 segment.map.bytes = new Uint8Array(request.response); 29450 return finishProcessingFn(null, segment); 29451 }; 29452}; 29453 29454/** 29455 * Response handler for segment-requests being sure to set the correct
29456 * property depending on whether the segment is encryped or not 29457 * Also records and keeps track of stats that are used for ABR purposes 29458 * 29459 * @param {Object} segment - a simplified copy of the segmentInfo object 29460 * from SegmentLoader 29461 * @param {Function} finishProcessingFn - a callback to execute to continue processing 29462 * this request 29463 */ 29464var handleSegmentResponse = function handleSegmentResponse(segment, finishProcessingFn) { 29465 return function (error, request) { 29466 var response = request.response; 29467 var errorObj = handleErrors(error, request); 29468 29469 if (errorObj) { 29470 return finishProcessingFn(errorObj, segment); 29471 } 29472 29473 // stop processing if received empty content 29474 if (response.byteLength === 0) { 29475 return finishProcessingFn({ 29476 status: request.status, 29477 message: 'Empty HLS segment content at URL: ' + request.uri, 29478 code: REQUEST_ERRORS.FAILURE, 29479 xhr: request 29480 }, segment); 29481 } 29482 29483 segment.stats = getRequestStats(request); 29484 29485 if (segment.key) { 29486 segment.encryptedBytes = new Uint8Array(request.response); 29487 } else { 29488 segment.bytes = new Uint8Array(request.response); 29489 } 29490 29491 return finishProcessingFn(null, segment); 29492 }; 29493}; 29494 29495/** 29496 * Decrypt the segment via the decryption web worker 29497 * 29498 * @param {WebWorker} decrypter - a WebWorker interface to AES-128 decryption routines 29499 * @param {Object} segment - a simplified copy of the segmentInfo object 29500 * from SegmentLoader 29501 * @param {Function} doneFn - a callback that is executed after decryption has completed 29502 */ 29503var decryptSegment = function decryptSegment(decrypter, segment, doneFn) { 29504 var decryptionHandler = function decryptionHandler(event) { 29505 if (event.data.source === segment.requestId) { 29506 decrypter.removeEventListener('message', decryptionHandler); 29507 var decrypted = event.data.decrypted; 29508 29509 segment.bytes = new Uint8Array(decrypted.bytes, decrypted.byteOffset, decrypted.byteLength); 29510 return doneFn(null, segment); 29511 } 29512 }; 29513 29514 decrypter.addEventListener('message', decryptionHandler); 29515 29516 // this is an encrypted segment 29517 // incrementally decrypt the segment 29518 decrypter.postMessage((0, _binUtils.createTransferableMessage)({ 29519 source: segment.requestId, 29520 encrypted: segment.encryptedBytes, 29521 key: segment.key.bytes, 29522 iv: segment.key.iv 29523 }), [segment.encryptedBytes.buffer, segment.key.bytes.buffer]); 29524}; 29525 29526/** 29527 * The purpose of this function is to get the most pertinent error from the 29528 * array of errors. 29529 * For instance if a timeout and two aborts occur, then the aborts were 29530 * likely triggered by the timeout so return that error object. 29531 */ 29532var getMostImportantError = function getMostImportantError(errors) { 29533 return errors.reduce(function (prev, err) { 29534 return err.code > prev.code ? err : prev; 29535 }); 29536}; 29537 29538/** 29539 * This function waits for all XHRs to finish (with either success or failure) 29540 * before continueing processing via it's callback. The function gathers errors 29541 * from each request into a single errors array so that the error status for 29542 * each request can be examined later. 29543 * 29544 * @param {Object} activeXhrs - an object that tracks all XHR requests 29545 * @param {WebWorker} decrypter - a WebWorker interface to AES-128 decryption routines 29546 * @param {Function} doneFn - a callback that is executed after all resources have been 29547 * downloaded and any decryption completed 29548 */ 29549var waitForCompletion = function waitForCompletion(activeXhrs, decrypter, doneFn) { 29550 var errors = []; 29551 var count = 0; 29552 29553 return function (error, segment) { 29554 if (error) { 29555 // If there are errors, we have to abort any outstanding requests 29556 abortAll(activeXhrs); 29557 errors.push(error); 29558 } 29559 count += 1; 29560 29561 if (count === activeXhrs.length) { 29562 // Keep track of when *all* of the requests have completed 29563 segment.endOfAllRequests = Date.now(); 29564 29565 if (errors.length > 0) { 29566 var worstError = getMostImportantError(errors); 29567 29568 return doneFn(worstError, segment); 29569 } 29570 if (segment.encryptedBytes) { 29571 return decryptSegment(decrypter, segment, doneFn); 29572 } 29573 // Otherwise, everything is ready just continue 29574 return doneFn(null, segment); 29575 } 29576 }; 29577}; 29578 29579/** 29580 * Simple progress event callback handler that gathers some stats before 29581 * executing a provided callback with the `segment` object 29582 * 29583 * @param {Object} segment - a simplified copy of the segmentInfo object 29584 * from SegmentLoader 29585 * @param {Function} progressFn - a callback that is executed each time a progress event 29586 * is received 29587 * @param {Event} event - the progress event object from XMLHttpRequest 29588 */ 29589var handleProgress = function handleProgress(segment, progressFn) { 29590 return function (event) { 29591 segment.stats = _videoJs2['default'].mergeOptions(segment.stats, getProgressStats(event)); 29592 29593 // record the time that we receive the first byte of data 29594 if (!segment.stats.firstBytesReceivedAt && segment.stats.bytesReceived) { 29595 segment.stats.firstBytesReceivedAt = Date.now(); 29596 } 29597 29598 return progressFn(event, segment); 29599 }; 29600}; 29601 29602/** 29603 * Load all resources and does any processing necessary for a media-segment 29604 * 29605 * Features: 29606 * decrypts the media-segment if it has a key uri and an iv 29607 * aborts *all* requests if *any* one request fails 29608 * 29609 * The segment object, at minimum, has the following format: 29610 * { 29611 * resolvedUri: String, 29612 * [byterange]: { 29613 * offset: Number, 29614 * length: Number 29615 * }, 29616 * [key]: { 29617 * resolvedUri: String 29618 * [byterange]: { 29619 * offset: Number, 29620 * length: Number 29621 * }, 29622 * iv: { 29623 * bytes: Uint32Array 29624 * } 29625 * }, 29626 * [map]: { 29627 * resolvedUri: String, 29628 * [byterange]: { 29629 * offset: Number, 29630 * length: Number 29631 * }, 29632 * [bytes]: Uint8Array 29633 * } 29634 * } 29635 * ...where [name] denotes optional properties 29636 * 29637 * @param {Function} xhr - an instance of the xhr wrapper in xhr.js 29638 * @param {Object} xhrOptions - the base options to provide to all xhr requests 29639 * @param {WebWorker} decryptionWorker - a WebWorker interface to AES-128 29640 * decryption routines 29641 * @param {Object} segment - a simplified copy of the segmentInfo object 29642 * from SegmentLoader 29643 * @param {Function} progressFn - a callback that receives progress events from the main 29644 * segment's xhr request 29645 * @param {Function} doneFn - a callback that is executed only once all requests have 29646 * succeeded or failed 29647 * @returns {Function} a function that, when invoked, immediately aborts all 29648 * outstanding requests 29649 */ 29650var mediaSegmentRequest = function mediaSegmentRequest(xhr, xhrOptions, decryptionWorker, segment, progressFn, doneFn) { 29651 var activeXhrs = [];
29652 var finishProcessingFn = waitForCompletion(activeXhrs, decryptionWorker, doneFn); 29653 29654 // optionally, request the decryption key 29655 if (segment.key) { 29656 var keyRequestOptions = _videoJs2['default'].mergeOptions(xhrOptions, { 29657 uri: segment.key.resolvedUri, 29658 responseType: 'arraybuffer' 29659 }); 29660 var keyRequestCallback = handleKeyResponse(segment, finishProcessingFn); 29661 var keyXhr = xhr(keyRequestOptions, keyRequestCallback); 29662 29663 activeXhrs.push(keyXhr); 29664 } 29665 29666 // optionally, request the associated media init segment 29667 if (segment.map && !segment.map.bytes) { 29668 var initSegmentOptions = _videoJs2['default'].mergeOptions(xhrOptions, { 29669 uri: segment.map.resolvedUri, 29670 responseType: 'arraybuffer', 29671 headers: segmentXhrHeaders(segment.map) 29672 }); 29673 var initSegmentRequestCallback = handleInitSegmentResponse(segment, finishProcessingFn); 29674 var initSegmentXhr = xhr(initSegmentOptions, initSegmentRequestCallback); 29675 29676 activeXhrs.push(initSegmentXhr); 29677 } 29678 29679 var segmentRequestOptions = _videoJs2['default'].mergeOptions(xhrOptions, { 29680 uri: segment.resolvedUri, 29681 responseType: 'arraybuffer', 29682 headers: segmentXhrHeaders(segment) 29683 }); 29684 var segmentRequestCallback = handleSegmentResponse(segment, finishProcessingFn); 29685 var segmentXhr = xhr(segmentRequestOptions, segmentRequestCallback); 29686 29687 segmentXhr.addEventListener('progress', handleProgress(segment, progressFn)); 29688 activeXhrs.push(segmentXhr); 29689 29690 return function () { 29691 return abortAll(activeXhrs); 29692 }; 29693}; 29694exports.mediaSegmentRequest = mediaSegmentRequest; 29695}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 29696},{"./bin-utils":2}],8:[function(require,module,exports){ 29697(function (global){ 29698/** 29699 * @file playback-watcher.js 29700 * 29701 * Playback starts, and now my watch begins. It shall not end until my death. I shall 29702 * take no wait, hold no uncleared timeouts, father no bad seeks. I shall wear no crowns 29703 * and win no glory. I shall live and die at my post. I am the corrector of the underflow. 29704 * I am the watcher of gaps. I am the shield that guards the realms of seekable. I pledge 29705 * my life and honor to the Playback Watch, for this Player and all the Players to come. 29706 */ 29707 29708'use strict'; 29709 29710Object.defineProperty(exports, '__esModule', { 29711 value: true 29712}); 29713 29714var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 29715 29716function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 29717 29718function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 29719 29720var _globalWindow = require('global/window'); 29721 29722var _globalWindow2 = _interopRequireDefault(_globalWindow); 29723 29724var _ranges = require('./ranges'); 29725 29726var _ranges2 = _interopRequireDefault(_ranges); 29727 29728var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null); 29729 29730var _videoJs2 = _interopRequireDefault(_videoJs); 29731 29732// Set of events that reset the playback-watcher time check logic and clear the timeout 29733var timerCancelEvents = ['seeking', 'seeked', 'pause', 'playing', 'error']; 29734 29735/** 29736 * @class PlaybackWatcher 29737 */ 29738 29739var PlaybackWatcher = (function () { 29740 /** 29741 * Represents an PlaybackWatcher object. 29742 * @constructor 29743 * @param {object} options an object that includes the tech and settings 29744 */ 29745 29746 function PlaybackWatcher(options) { 29747 var _this = this; 29748 29749 _classCallCheck(this, PlaybackWatcher); 29750 29751 this.tech_ = options.tech; 29752 this.seekable = options.seekable; 29753 29754 this.consecutiveUpdates = 0; 29755 this.lastRecordedTime = null; 29756 this.timer_ = null; 29757 this.checkCurrentTimeTimeout_ = null; 29758 29759 if (options.debug) { 29760 this.logger_ = _videoJs2['default'].log.bind(_videoJs2['default'], 'playback-watcher ->'); 29761 } 29762 this.logger_('initialize'); 29763 29764 var canPlayHandler = function canPlayHandler() { 29765 return _this.monitorCurrentTime_(); 29766 }; 29767 var waitingHandler = function waitingHandler() { 29768 return _this.techWaiting_(); 29769 }; 29770 var cancelTimerHandler = function cancelTimerHandler() { 29771 return _this.cancelTimer_(); 29772 }; 29773 var fixesBadSeeksHandler = function fixesBadSeeksHandler() { 29774 return _this.fixesBadSeeks_(); 29775 }; 29776 29777 this.tech_.on('seekablechanged', fixesBadSeeksHandler); 29778 this.tech_.on('waiting', waitingHandler); 29779 this.tech_.on(timerCancelEvents, cancelTimerHandler); 29780 this.tech_.on('canplay', canPlayHandler); 29781 29782 // Define the dispose function to clean up our events 29783 this.dispose = function () { 29784 _this.logger_('dispose'); 29785 _this.tech_.off('seekablechanged', fixesBadSeeksHandler); 29786 _this.tech_.off('waiting', waitingHandler); 29787 _this.tech_.off(timerCancelEvents, cancelTimerHandler); 29788 _this.tech_.off('canplay', canPlayHandler); 29789 if (_this.checkCurrentTimeTimeout_) { 29790 _globalWindow2['default'].clearTimeout(_this.checkCurrentTimeTimeout_); 29791 } 29792 _this.cancelTimer_(); 29793 }; 29794 } 29795 29796 /** 29797 * Periodically check current time to see if playback stopped 29798 * 29799 * @private 29800 */ 29801 29802 _createClass(PlaybackWatcher, [{ 29803 key: 'monitorCurrentTime_', 29804 value: function monitorCurrentTime_() { 29805 this.checkCurrentTime_(); 29806 29807 if (this.checkCurrentTimeTimeout_) { 29808 _globalWindow2['default'].clearTimeout(this.checkCurrentTimeTimeout_); 29809 } 29810 29811 // 42 = 24 fps // 250 is what Webkit uses // FF uses 15 29812 this.checkCurrentTimeTimeout_ = _globalWindow2['default'].setTimeout(this.monitorCurrentTime_.bind(this), 250); 29813 } 29814 29815 /** 29816 * The purpose of this function is to emulate the "waiting" event on 29817 * browsers that do not emit it when they are waiting for more 29818 * data to continue playback 29819 * 29820 * @private 29821 */ 29822 }, { 29823 key: 'checkCurrentTime_', 29824 value: function checkCurrentTime_() { 29825 if (this.tech_.seeking() && this.fixesBadSeeks_()) { 29826 this.consecutiveUpdates = 0; 29827 this.lastRecordedTime = this.tech_.currentTime(); 29828 return; 29829 } 29830 29831 if (this.tech_.paused() || this.tech_.seeking()) { 29832 return; 29833 } 29834 29835 var currentTime = this.tech_.currentTime(); 29836 var buffered = this.tech_.buffered(); 29837 29838 if (this.lastRecordedTime === currentTime && (!buffered.length || currentTime + 0.1 >= buffered.end(buffered.length - 1))) { 29839 // If current time is at the end of the final buffered region, then any playback 29840 // stall is most likely caused by buffering in a low bandwidth environment. The tech 29841 // should fire a `waiting` event in this scenario, but due to browser and tech
vendor: 6,639 bytes, lines 29842-30018
29842 // inconsistencies (e.g. The Flash tech does not fire a `waiting` event when the end 29843 // of the buffer is reached and has fallen off the live window). Calling 29844 // `techWaiting_` here allows us to simulate responding to a native `waiting` event 29845 // when the tech fails to emit one. 29846 return this.techWaiting_(); 29847 } 29848 29849 if (this.consecutiveUpdates >= 5 && currentTime === this.lastRecordedTime) { 29850 this.consecutiveUpdates++; 29851 this.waiting_(); 29852 } else if (currentTime === this.lastRecordedTime) { 29853 this.consecutiveUpdates++; 29854 } else { 29855 this.consecutiveUpdates = 0; 29856 this.lastRecordedTime = currentTime; 29857 } 29858 } 29859 29860 /** 29861 * Cancels any pending timers and resets the 'timeupdate' mechanism 29862 * designed to detect that we are stalled 29863 * 29864 * @private 29865 */ 29866 }, { 29867 key: 'cancelTimer_', 29868 value: function cancelTimer_() { 29869 this.consecutiveUpdates = 0; 29870 29871 if (this.timer_) { 29872 this.logger_('cancelTimer_'); 29873 clearTimeout(this.timer_); 29874 } 29875 29876 this.timer_ = null; 29877 } 29878 29879 /** 29880 * Fixes situations where there's a bad seek 29881 * 29882 * @return {Boolean} whether an action was taken to fix the seek 29883 * @private 29884 */ 29885 }, { 29886 key: 'fixesBadSeeks_', 29887 value: function fixesBadSeeks_() { 29888 var seeking = this.tech_.seeking(); 29889 var seekable = this.seekable(); 29890 var currentTime = this.tech_.currentTime(); 29891 var seekTo = undefined; 29892 29893 if (seeking && this.afterSeekableWindow_(seekable, currentTime)) { 29894 var seekableEnd = seekable.end(seekable.length - 1); 29895 29896 // sync to live point (if VOD, our seekable was updated and we're simply adjusting) 29897 seekTo = seekableEnd; 29898 } 29899 29900 if (seeking && this.beforeSeekableWindow_(seekable, currentTime)) { 29901 var seekableStart = seekable.start(0); 29902 29903 // sync to the beginning of the live window 29904 // provide a buffer of .1 seconds to handle rounding/imprecise numbers 29905 seekTo = seekableStart + 0.1; 29906 } 29907 29908 if (typeof seekTo !== 'undefined') { 29909 this.logger_('Trying to seek outside of seekable at time ' + currentTime + ' with ' + ('seekable range ' + _ranges2['default'].printableRange(seekable) + '. Seeking to ') + (seekTo + '.')); 29910 29911 this.tech_.setCurrentTime(seekTo); 29912 return true; 29913 } 29914 29915 return false; 29916 } 29917 29918 /** 29919 * Handler for situations when we determine the player is waiting. 29920 * 29921 * @private 29922 */ 29923 }, { 29924 key: 'waiting_', 29925 value: function waiting_() { 29926 if (this.techWaiting_()) { 29927 return; 29928 } 29929 29930 // All tech waiting checks failed. Use last resort correction 29931 var currentTime = this.tech_.currentTime(); 29932 var buffered = this.tech_.buffered(); 29933 var currentRange = _ranges2['default'].findRange(buffered, currentTime); 29934 29935 // Sometimes the player can stall for unknown reasons within a contiguous buffered 29936 // region with no indication that anything is amiss (seen in Firefox). Seeking to 29937 // currentTime is usually enough to kickstart the player. This checks that the player 29938 // is currently within a buffered region before attempting a corrective seek. 29939 // Chrome does not appear to continue `timeupdate` events after a `waiting` event 29940 // until there is ~ 3 seconds of forward buffer available. PlaybackWatcher should also 29941 // make sure there is ~3 seconds of forward buffer before taking any corrective action 29942 // to avoid triggering an `unknownwaiting` event when the network is slow. 29943 if (currentRange.length && currentTime + 3 <= currentRange.end(0)) { 29944 this.cancelTimer_(); 29945 this.tech_.setCurrentTime(currentTime); 29946 29947 this.logger_('Stopped at ' + currentTime + ' while inside a buffered region ' + ('[' + currentRange.start(0) + ' -> ' + currentRange.end(0) + ']. Attempting to resume ') + 'playback by seeking to the current time.'); 29948 29949 // unknown waiting corrections may be useful for monitoring QoS 29950 this.tech_.trigger({ type: 'usage', name: 'hls-unknown-waiting' }); 29951 return; 29952 } 29953 } 29954 29955 /** 29956 * Handler for situations when the tech fires a `waiting` event 29957 * 29958 * @return {Boolean} 29959 * True if an action (or none) was needed to correct the waiting. False if no 29960 * checks passed 29961 * @private 29962 */ 29963 }, { 29964 key: 'techWaiting_', 29965 value: function techWaiting_() { 29966 var seekable = this.seekable(); 29967 var currentTime = this.tech_.currentTime(); 29968 29969 if (this.tech_.seeking() && this.fixesBadSeeks_()) { 29970 // Tech is seeking or bad seek fixed, no action needed 29971 return true; 29972 } 29973 29974 if (this.tech_.seeking() || this.timer_ !== null) { 29975 // Tech is seeking or already waiting on another action, no action needed 29976 return true; 29977 } 29978 29979 if (this.beforeSeekableWindow_(seekable, currentTime)) { 29980 var livePoint = seekable.end(seekable.length - 1); 29981 29982 this.logger_('Fell out of live window at time ' + currentTime + '. Seeking to ' + ('live point (seekable end) ' + livePoint)); 29983 this.cancelTimer_(); 29984 this.tech_.setCurrentTime(livePoint); 29985 29986 // live window resyncs may be useful for monitoring QoS 29987 this.tech_.trigger({ type: 'usage', name: 'hls-live-resync' }); 29988 return true; 29989 } 29990 29991 var buffered = this.tech_.buffered(); 29992 var nextRange = _ranges2['default'].findNextRange(buffered, currentTime); 29993 29994 if (this.videoUnderflow_(nextRange, buffered, currentTime)) { 29995 // Even though the video underflowed and was stuck in a gap, the audio overplayed 29996 // the gap, leading currentTime into a buffered range. Seeking to currentTime 29997 // allows the video to catch up to the audio position without losing any audio 29998 // (only suffering ~3 seconds of frozen video and a pause in audio playback). 29999 this.cancelTimer_(); 30000 this.tech_.setCurrentTime(currentTime); 30001 30002 // video underflow may be useful for monitoring QoS 30003 this.tech_.trigger({ type: 'usage', name: 'hls-video-underflow' }); 30004 return true; 30005 } 30006 30007 // check for gap 30008 if (nextRange.length > 0) { 30009 var difference = nextRange.start(0) - currentTime; 30010 30011 this.logger_('Stopped at ' + currentTime + ', setting timer for ' + difference + ', seeking ' + ('to ' + nextRange.start(0))); 30012 30013 this.timer_ = setTimeout(this.skipTheGap_.bind(this), difference * 1000, currentTime); 30014 return true; 30015 } 30016 30017 // All checks failed. Returning false to indicate failure to correct waiting 30018 return false;
30019 } 30020 }, { 30021 key: 'afterSeekableWindow_', 30022 value: function afterSeekableWindow_(seekable, currentTime) { 30023 if (!seekable.length) { 30024 // we can't make a solid case if there's no seekable, default to false 30025 return false; 30026 } 30027 30028 // provide a buffer of .1 seconds to handle rounding/imprecise numbers 30029 if (currentTime > seekable.end(seekable.length - 1) + 0.1) { 30030 return true; 30031 } 30032 30033 return false; 30034 } 30035 }, { 30036 key: 'beforeSeekableWindow_', 30037 value: function beforeSeekableWindow_(seekable, currentTime) { 30038 if (seekable.length && 30039 // can't fall before 0 and 0 seekable start identifies VOD stream 30040 seekable.start(0) > 0 && 30041 // provide a buffer of .1 seconds to handle rounding/imprecise numbers 30042 currentTime < seekable.start(0) - 0.1) { 30043 return true; 30044 } 30045 30046 return false; 30047 } 30048 }, { 30049 key: 'videoUnderflow_', 30050 value: function videoUnderflow_(nextRange, buffered, currentTime) { 30051 if (nextRange.length === 0) { 30052 // Even if there is no available next range, there is still a possibility we are 30053 // stuck in a gap due to video underflow. 30054 var gap = this.gapFromVideoUnderflow_(buffered, currentTime); 30055 30056 if (gap) { 30057 this.logger_('Encountered a gap in video from ' + gap.start + ' to ' + gap.end + '. ' + ('Seeking to current time ' + currentTime)); 30058 30059 return true; 30060 } 30061 } 30062 30063 return false; 30064 } 30065 30066 /** 30067 * Timer callback. If playback still has not proceeded, then we seek 30068 * to the start of the next buffered region. 30069 * 30070 * @private 30071 */ 30072 }, { 30073 key: 'skipTheGap_', 30074 value: function skipTheGap_(scheduledCurrentTime) { 30075 var buffered = this.tech_.buffered(); 30076 var currentTime = this.tech_.currentTime(); 30077 var nextRange = _ranges2['default'].findNextRange(buffered, currentTime); 30078 30079 this.cancelTimer_(); 30080 30081 if (nextRange.length === 0 || currentTime !== scheduledCurrentTime) { 30082 return; 30083 } 30084 30085 this.logger_('skipTheGap_:', 'currentTime:', currentTime, 'scheduled currentTime:', scheduledCurrentTime, 'nextRange start:', nextRange.start(0)); 30086 30087 // only seek if we still have not played 30088 this.tech_.setCurrentTime(nextRange.start(0) + _ranges2['default'].TIME_FUDGE_FACTOR); 30089 30090 this.tech_.trigger({ type: 'usage', name: 'hls-gap-skip' }); 30091 } 30092 }, { 30093 key: 'gapFromVideoUnderflow_', 30094 value: function gapFromVideoUnderflow_(buffered, currentTime) { 30095 // At least in Chrome, if there is a gap in the video buffer, the audio will continue 30096 // playing for ~3 seconds after the video gap starts. This is done to account for 30097 // video buffer underflow/underrun (note that this is not done when there is audio 30098 // buffer underflow/underrun -- in that case the video will stop as soon as it 30099 // encounters the gap, as audio stalls are more noticeable/jarring to a user than 30100 // video stalls). The player's time will reflect the playthrough of audio, so the 30101 // time will appear as if we are in a buffered region, even if we are stuck in a 30102 // "gap." 30103 // 30104 // Example: 30105 // video buffer: 0 => 10.1, 10.2 => 20 30106 // audio buffer: 0 => 20 30107 // overall buffer: 0 => 10.1, 10.2 => 20 30108 // current time: 13 30109 // 30110 // Chrome's video froze at 10 seconds, where the video buffer encountered the gap, 30111 // however, the audio continued playing until it reached ~3 seconds past the gap 30112 // (13 seconds), at which point it stops as well. Since current time is past the 30113 // gap, findNextRange will return no ranges. 30114 // 30115 // To check for this issue, we see if there is a gap that starts somewhere within 30116 // a 3 second range (3 seconds +/- 1 second) back from our current time. 30117 var gaps = _ranges2['default'].findGaps(buffered); 30118 30119 for (var i = 0; i < gaps.length; i++) { 30120 var start = gaps.start(i); 30121 var end = gaps.end(i); 30122 30123 // gap is starts no more than 4 seconds back 30124 if (currentTime - start < 4 && currentTime - start > 2) { 30125 return { 30126 start: start, 30127 end: end 30128 }; 30129 } 30130 } 30131 30132 return null; 30133 } 30134 30135 /** 30136 * A debugging logger noop that is set to console.log only if debugging 30137 * is enabled globally 30138 * 30139 * @private 30140 */ 30141 }, { 30142 key: 'logger_', 30143 value: function logger_() {} 30144 }]); 30145 30146 return PlaybackWatcher; 30147})(); 30148 30149exports['default'] = PlaybackWatcher; 30150module.exports = exports['default']; 30151}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 30152},{"./ranges":12,"global/window":32}],9:[function(require,module,exports){ 30153(function (global){ 30154/** 30155 * @file playlist-loader.js 30156 * 30157 * A state machine that manages the loading, caching, and updating of 30158 * M3U8 playlists. 30159 * 30160 */ 30161'use strict'; 30162 30163Object.defineProperty(exports, '__esModule', { 30164 value: true 30165}); 30166 30167function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 30168 30169var _resolveUrl = require('./resolve-url'); 30170 30171var _resolveUrl2 = _interopRequireDefault(_resolveUrl); 30172 30173var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null); 30174 30175var _playlistJs = require('./playlist.js'); 30176 30177var _m3u8Parser = require('m3u8-parser'); 30178 30179var _m3u8Parser2 = _interopRequireDefault(_m3u8Parser); 30180 30181var _globalWindow = require('global/window'); 30182 30183var _globalWindow2 = _interopRequireDefault(_globalWindow); 30184 30185/** 30186 * Returns a new array of segments that is the result of merging 30187 * properties from an older list of segments onto an updated 30188 * list. No properties on the updated playlist will be overridden. 30189 * 30190 * @param {Array} original the outdated list of segments 30191 * @param {Array} update the updated list of segments 30192 * @param {Number=} offset the index of the first update 30193 * segment in the original segment list. For non-live playlists, 30194 * this should always be zero and does not need to be 30195 * specified. For live playlists, it should be the difference 30196 * between the media sequence numbers in the original and updated 30197 * playlists. 30198 * @return a list of merged segment objects 30199 */ 30200var updateSegments = function updateSegments(original, update, offset) { 30201 var result = update.slice(); 30202 var length = undefined; 30203 var i = undefined; 30204 30205 offset = offset || 0; 30206 length = Math.min(original.length, update.length + offset); 30207 30208 for (i = offset; i < length; i++) { 30209 result[i - offset] = (0, _videoJs.mergeOptions)(original[i], result[i - offset]); 30210 } 30211 return result; 30212}; 30213 30214/** 30215 * Returns a new master playlist that is the result of merging an 30216 * updated media playlist into the original version. If the 30217 * updated media playlist does not match any of the playlist 30218 * entries in the original master playlist, null is returned. 30219 * 30220 * @param {Object} master a parsed master M3U8 object 30221 * @param {Object} media a parsed media M3U8 object 30222 * @return {Object} a new object that represents the original 30223 * master playlist with the updated media playlist merged in, or 30224 * null if the merge produced no change. 30225 */ 30226var updateMaster = function updateMaster(master, media) { 30227 var changed = false;
30228 var result = (0, _videoJs.mergeOptions)(master, {}); 30229 var i = master.playlists.length; 30230 var playlist = undefined; 30231 var segment = undefined; 30232 var j = undefined; 30233 30234 while (i--) { 30235 playlist = result.playlists[i]; 30236 if (playlist.uri === media.uri) { 30237 // consider the playlist unchanged if the number of segments 30238 // are equal and the media sequence number is unchanged 30239 if (playlist.segments && media.segments && playlist.segments.length === media.segments.length && playlist.mediaSequence === media.mediaSequence) { 30240 continue; 30241 } 30242 30243 result.playlists[i] = (0, _videoJs.mergeOptions)(playlist, media); 30244 result.playlists[media.uri] = result.playlists[i]; 30245 30246 // if the update could overlap existing segment information, 30247 // merge the two lists 30248 if (playlist.segments) { 30249 result.playlists[i].segments = updateSegments(playlist.segments, media.segments, media.mediaSequence - playlist.mediaSequence); 30250 } 30251 // resolve any missing segment and key URIs 30252 j = 0; 30253 if (result.playlists[i].segments) { 30254 j = result.playlists[i].segments.length; 30255 } 30256 while (j--) { 30257 segment = result.playlists[i].segments[j]; 30258 if (!segment.resolvedUri) { 30259 segment.resolvedUri = (0, _resolveUrl2['default'])(playlist.resolvedUri, segment.uri); 30260 } 30261 if (segment.key && !segment.key.resolvedUri) { 30262 segment.key.resolvedUri = (0, _resolveUrl2['default'])(playlist.resolvedUri, segment.key.uri); 30263 } 30264 if (segment.map && !segment.map.resolvedUri) { 30265 segment.map.resolvedUri = (0, _resolveUrl2['default'])(playlist.resolvedUri, segment.map.uri); 30266 } 30267 } 30268 changed = true; 30269 } 30270 } 30271 return changed ? result : null; 30272}; 30273 30274/** 30275 * Load a playlist from a remote location 30276 * 30277 * @class PlaylistLoader 30278 * @extends Stream 30279 * @param {String} srcUrl the url to start with 30280 * @param {Boolean} withCredentials the withCredentials xhr option 30281 * @constructor 30282 */ 30283var PlaylistLoader = function PlaylistLoader(srcUrl, hls, withCredentials) { 30284 var _this = this; 30285 30286 /* eslint-disable consistent-this */ 30287 var loader = this; 30288 /* eslint-enable consistent-this */ 30289 var mediaUpdateTimeout = undefined; 30290 var request = undefined; 30291 var playlistRequestError = undefined; 30292 var haveMetadata = undefined; 30293 30294 PlaylistLoader.prototype.constructor.call(this); 30295 30296 this.hls_ = hls; 30297 30298 if (!srcUrl) { 30299 throw new Error('A non-empty playlist URL is required'); 30300 } 30301 30302 playlistRequestError = function (xhr, url, startingState) { 30303 loader.setBandwidth(request || xhr); 30304 30305 // any in-flight request is now finished 30306 request = null; 30307 30308 if (startingState) { 30309 loader.state = startingState; 30310 } 30311 30312 loader.error = { 30313 playlist: loader.master.playlists[url], 30314 status: xhr.status, 30315 message: 'HLS playlist request error at URL: ' + url, 30316 responseText: xhr.responseText, 30317 code: xhr.status >= 500 ? 4 : 2 30318 }; 30319 30320 loader.trigger('error'); 30321 }; 30322 30323 // update the playlist loader's state in response to a new or 30324 // updated playlist. 30325 haveMetadata = function (xhr, url) { 30326 var parser = undefined; 30327 var refreshDelay = undefined; 30328 var update = undefined; 30329 30330 loader.setBandwidth(request || xhr); 30331 30332 // any in-flight request is now finished 30333 request = null; 30334 30335 loader.state = 'HAVE_METADATA'; 30336 30337 parser = new _m3u8Parser2['default'].Parser(); 30338 parser.push(xhr.responseText); 30339 parser.end(); 30340 parser.manifest.uri = url; 30341 // m3u8-parser does not attach an attributes property to media playlists so make 30342 // sure that the property is attached to avoid undefined reference errors 30343 parser.manifest.attributes = parser.manifest.attributes || {}; 30344 30345 // merge this playlist into the master 30346 update = updateMaster(loader.master, parser.manifest); 30347 refreshDelay = (parser.manifest.targetDuration || 10) * 1000; 30348 loader.targetDuration = parser.manifest.targetDuration; 30349 if (update) { 30350 loader.master = update; 30351 loader.media_ = loader.master.playlists[parser.manifest.uri]; 30352 } else { 30353 // if the playlist is unchanged since the last reload, 30354 // try again after half the target duration 30355 refreshDelay /= 2; 30356 loader.trigger('playlistunchanged'); 30357 } 30358 30359 // refresh live playlists after a target duration passes 30360 if (!loader.media().endList) { 30361 _globalWindow2['default'].clearTimeout(mediaUpdateTimeout); 30362 mediaUpdateTimeout = _globalWindow2['default'].setTimeout(function () { 30363 loader.trigger('mediaupdatetimeout'); 30364 }, refreshDelay); 30365 } 30366 30367 loader.trigger('loadedplaylist'); 30368 }; 30369 30370 // initialize the loader state 30371 loader.state = 'HAVE_NOTHING'; 30372 30373 /** 30374 * Abort any outstanding work and clean up. 30375 */ 30376 loader.dispose = function () { 30377 loader.stopRequest(); 30378 _globalWindow2['default'].clearTimeout(mediaUpdateTimeout); 30379 loader.off(); 30380 }; 30381 30382 loader.stopRequest = function () { 30383 if (request) { 30384 var oldRequest = request; 30385 30386 request = null; 30387 oldRequest.onreadystatechange = null; 30388 oldRequest.abort(); 30389 } 30390 }; 30391 30392 /** 30393 * Returns the number of enabled playlists on the master playlist object 30394 * 30395 * @return {Number} number of eneabled playlists 30396 */ 30397 loader.enabledPlaylists_ = function () { 30398 return loader.master.playlists.filter(_playlistJs.isEnabled).length; 30399 }; 30400 30401 /** 30402 * Returns whether the current playlist is the lowest rendition 30403 * 30404 * @return {Boolean} true if on lowest rendition 30405 */ 30406 loader.isLowestEnabledRendition_ = function () { 30407 if (loader.master.playlists.length === 1) { 30408 return true; 30409 } 30410 30411 var media = loader.media(); 30412 30413 var currentBandwidth = media.attributes.BANDWIDTH || Number.MAX_VALUE; 30414 30415 return loader.master.playlists.filter(function (playlist) { 30416 var enabled = (0, _playlistJs.isEnabled)(playlist); 30417 30418 if (!enabled) { 30419 return false;
30420 } 30421 30422 return (playlist.attributes.BANDWIDTH || 0) < currentBandwidth; 30423 }).length === 0; 30424 }; 30425 30426 /** 30427 * Returns whether the current playlist is the final available rendition 30428 * 30429 * @return {Boolean} true if on final rendition 30430 */ 30431 loader.isFinalRendition_ = function () { 30432 return loader.master.playlists.filter(_playlistJs.isEnabled).length === 1; 30433 }; 30434 30435 /** 30436 * When called without any arguments, returns the currently 30437 * active media playlist. When called with a single argument, 30438 * triggers the playlist loader to asynchronously switch to the 30439 * specified media playlist. Calling this method while the 30440 * loader is in the HAVE_NOTHING causes an error to be emitted 30441 * but otherwise has no effect. 30442 * 30443 * @param {Object=} playlist the parsed media playlist 30444 * object to switch to 30445 * @return {Playlist} the current loaded media 30446 */ 30447 loader.media = function (playlist) { 30448 var startingState = loader.state; 30449 var mediaChange = undefined; 30450 30451 // getter 30452 if (!playlist) { 30453 return loader.media_; 30454 } 30455 30456 // setter 30457 if (loader.state === 'HAVE_NOTHING') { 30458 throw new Error('Cannot switch media playlist from ' + loader.state); 30459 } 30460 30461 // find the playlist object if the target playlist has been 30462 // specified by URI 30463 if (typeof playlist === 'string') { 30464 if (!loader.master.playlists[playlist]) { 30465 throw new Error('Unknown playlist URI: ' + playlist); 30466 } 30467 playlist = loader.master.playlists[playlist]; 30468 } 30469 30470 mediaChange = !loader.media_ || playlist.uri !== loader.media_.uri; 30471 30472 // switch to fully loaded playlists immediately 30473 if (loader.master.playlists[playlist.uri].endList) { 30474 // abort outstanding playlist requests 30475 if (request) { 30476 request.onreadystatechange = null; 30477 request.abort(); 30478 request = null; 30479 } 30480 loader.state = 'HAVE_METADATA'; 30481 loader.media_ = playlist; 30482 30483 // trigger media change if the active media has been updated 30484 if (mediaChange) { 30485 loader.trigger('mediachanging'); 30486 loader.trigger('mediachange'); 30487 } 30488 return; 30489 } 30490 30491 // switching to the active playlist is a no-op 30492 if (!mediaChange) { 30493 return; 30494 } 30495 30496 loader.state = 'SWITCHING_MEDIA'; 30497 30498 // there is already an outstanding playlist request 30499 if (request) { 30500 if ((0, _resolveUrl2['default'])(loader.master.uri, playlist.uri) === request.url) { 30501 // requesting to switch to the same playlist multiple times 30502 // has no effect after the first 30503 return; 30504 } 30505 request.onreadystatechange = null; 30506 request.abort(); 30507 request = null; 30508 } 30509 30510 // request the new playlist 30511 if (this.media_) { 30512 this.trigger('mediachanging'); 30513 } 30514 30515 request = this.hls_.xhr({ 30516 uri: (0, _resolveUrl2['default'])(loader.master.uri, playlist.uri), 30517 withCredentials: withCredentials 30518 }, function (error, req) { 30519 // disposed 30520 if (!request) { 30521 return; 30522 } 30523 30524 if (error) { 30525 return playlistRequestError(request, playlist.uri, startingState); 30526 } 30527 30528 haveMetadata(req, playlist.uri); 30529 30530 // fire loadedmetadata the first time a media playlist is loaded 30531 if (startingState === 'HAVE_MASTER') { 30532 loader.trigger('loadedmetadata'); 30533 } else { 30534 loader.trigger('mediachange'); 30535 } 30536 }); 30537 }; 30538 30539 /** 30540 * set the bandwidth on an xhr to the bandwidth on the playlist 30541 */ 30542 loader.setBandwidth = function (xhr) { 30543 loader.bandwidth = xhr.bandwidth; 30544 }; 30545 30546 // live playlist staleness timeout 30547 loader.on('mediaupdatetimeout', function () { 30548 if (loader.state !== 'HAVE_METADATA') { 30549 // only refresh the media playlist if no other activity is going on 30550 return; 30551 } 30552 30553 loader.state = 'HAVE_CURRENT_METADATA'; 30554 30555 request = this.hls_.xhr({ 30556 uri: (0, _resolveUrl2['default'])(loader.master.uri, loader.media().uri), 30557 withCredentials: withCredentials 30558 }, function (error, req) { 30559 // disposed 30560 if (!request) { 30561 return; 30562 } 30563 30564 if (error) { 30565 return playlistRequestError(request, loader.media().uri, 'HAVE_METADATA'); 30566 } 30567 30568 haveMetadata(request, loader.media().uri); 30569 }); 30570 }); 30571 30572 /** 30573 * pause loading of the playlist 30574 */ 30575 loader.pause = function () { 30576 loader.stopRequest(); 30577 _globalWindow2['default'].clearTimeout(mediaUpdateTimeout); 30578 if (loader.state === 'HAVE_NOTHING') { 30579 // If we pause the loader before any data has been retrieved, its as if we never 30580 // started, so reset to an unstarted state. 30581 loader.started = false;
30582 } 30583 // Need to restore state now that no activity is happening 30584 if (loader.state === 'SWITCHING_MEDIA') { 30585 // if the loader was in the process of switching media, it should either return to 30586 // HAVE_MASTER or HAVE_METADATA depending on if the loader has loaded a media 30587 // playlist yet. This is determined by the existence of loader.media_ 30588 if (loader.media_) { 30589 loader.state = 'HAVE_METADATA'; 30590 } else { 30591 loader.state = 'HAVE_MASTER'; 30592 } 30593 } else if (loader.state === 'HAVE_CURRENT_METADATA') { 30594 loader.state = 'HAVE_METADATA'; 30595 } 30596 }; 30597 30598 /** 30599 * start loading of the playlist 30600 */ 30601 loader.load = function (isFinalRendition) { 30602 var media = loader.media(); 30603 30604 _globalWindow2['default'].clearTimeout(mediaUpdateTimeout); 30605 30606 if (isFinalRendition) { 30607 var refreshDelay = media ? media.targetDuration / 2 * 1000 : 5 * 1000; 30608 30609 mediaUpdateTimeout = _globalWindow2['default'].setTimeout(loader.load.bind(null, false), refreshDelay); 30610 return; 30611 } 30612 30613 if (!loader.started) { 30614 loader.start(); 30615 return; 30616 } 30617 30618 if (media && !media.endList) { 30619 loader.trigger('mediaupdatetimeout'); 30620 } else { 30621 loader.trigger('loadedplaylist'); 30622 } 30623 }; 30624 30625 /** 30626 * start loading of the playlist 30627 */ 30628 loader.start = function () { 30629 loader.started = true; 30630 30631 // request the specified URL 30632 request = _this.hls_.xhr({ 30633 uri: srcUrl, 30634 withCredentials: withCredentials 30635 }, function (error, req) { 30636 var parser = undefined; 30637 var playlist = undefined; 30638 var i = undefined; 30639 30640 // disposed 30641 if (!request) { 30642 return; 30643 } 30644 30645 // clear the loader's request reference 30646 request = null; 30647 30648 if (error) { 30649 loader.error = { 30650 status: req.status, 30651 message: 'HLS playlist request error at URL: ' + srcUrl, 30652 responseText: req.responseText, 30653 // MEDIA_ERR_NETWORK 30654 code: 2 30655 }; 30656 if (loader.state === 'HAVE_NOTHING') { 30657 loader.started = false; 30658 } 30659 return loader.trigger('error'); 30660 } 30661 30662 parser = new _m3u8Parser2['default'].Parser(); 30663 parser.push(req.responseText); 30664 parser.end(); 30665 30666 loader.state = 'HAVE_MASTER'; 30667 30668 parser.manifest.uri = srcUrl; 30669 30670 // loaded a master playlist 30671 if (parser.manifest.playlists) { 30672 loader.master = parser.manifest; 30673 30674 // setup by-URI lookups and resolve media playlist URIs 30675 i = loader.master.playlists.length; 30676 while (i--) { 30677 playlist = loader.master.playlists[i]; 30678 loader.master.playlists[playlist.uri] = playlist; 30679 playlist.resolvedUri = (0, _resolveUrl2['default'])(loader.master.uri, playlist.uri); 30680 30681 if (!playlist.attributes) { 30682 // Although the spec states an #EXT-X-STREAM-INF tag MUST have a 30683 // BANDWIDTH attribute, we can play the stream without it. This means a poorly 30684 // formatted master playlist may not have an attribute list. An attributes 30685 // property is added here to prevent undefined references when we encounter 30686 // this scenario. 30687 playlist.attributes = {}; 30688 30689 _videoJs.log.warn('Invalid playlist STREAM-INF detected. Missing BANDWIDTH attribute.'); 30690 } 30691 } 30692 30693 // resolve any media group URIs 30694 ['AUDIO', 'SUBTITLES'].forEach(function (mediaType) { 30695 for (var groupKey in loader.master.mediaGroups[mediaType]) { 30696 for (var labelKey in loader.master.mediaGroups[mediaType][groupKey]) { 30697 var mediaProperties = loader.master.mediaGroups[mediaType][groupKey][labelKey]; 30698 30699 if (mediaProperties.uri) { 30700 mediaProperties.resolvedUri = (0, _resolveUrl2['default'])(loader.master.uri, mediaProperties.uri); 30701 } 30702 } 30703 } 30704 }); 30705 30706 loader.trigger('loadedplaylist'); 30707 if (!request) { 30708 // no media playlist was specifically selected so start 30709 // from the first listed one 30710 loader.media(parser.manifest.playlists[0]); 30711 } 30712 return; 30713 } 30714 30715 // loaded a media playlist 30716 // infer a master playlist if none was previously requested 30717 loader.master = { 30718 mediaGroups: { 30719 'AUDIO': {}, 30720 'VIDEO': {}, 30721 'CLOSED-CAPTIONS': {}, 30722 'SUBTITLES': {} 30723 }, 30724 uri: _globalWindow2['default'].location.href, 30725 playlists: [{ 30726 uri: srcUrl 30727 }] 30728 }; 30729 loader.master.playlists[srcUrl] = loader.master.playlists[0]; 30730 loader.master.playlists[0].resolvedUri = srcUrl; 30731 // m3u8-parser does not attach an attributes property to media playlists so make 30732 // sure that the property is attached to avoid undefined reference errors 30733 loader.master.playlists[0].attributes = loader.master.playlists[0].attributes || {}; 30734 haveMetadata(req, srcUrl); 30735 return loader.trigger('loadedmetadata'); 30736 }); 30737 }; 30738}; 30739 30740PlaylistLoader.prototype = new _videoJs.EventTarget(); 30741 30742exports['default'] = PlaylistLoader; 30743module.exports = exports['default']; 30744}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 30745},{"./playlist.js":11,"./resolve-url":15,"global/window":32,"m3u8-parser":33}],10:[function(require,module,exports){ 30746'use strict'; 30747 30748Object.defineProperty(exports, '__esModule', { 30749 value: true 30750}); 30751 30752function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 30753 30754var _config = require('./config'); 30755 30756var _config2 = _interopRequireDefault(_config); 30757 30758var _playlist = require('./playlist'); 30759 30760var _playlist2 = _interopRequireDefault(_playlist); 30761 30762var _utilCodecsJs = require('./util/codecs.js'); 30763 30764// Utilities 30765 30766/** 30767 * Returns the CSS value for the specified property on an element 30768 * using `getComputedStyle`. Firefox has a long-standing issue where 30769 * getComputedStyle() may return null when running in an iframe with 30770 * `display: none`. 30771 * 30772 * @see https://bugzilla.mozilla.org/show_bug.cgi?id=548397 30773 * @param {HTMLElement} el the htmlelement to work on 30774 * @param {string} the proprety to get the style for 30775 */ 30776var safeGetComputedStyle = function safeGetComputedStyle(el, property) { 30777 var result = undefined; 30778 30779 if (!el) { 30780 return ''; 30781 } 30782 30783 result = window.getComputedStyle(el); 30784 if (!result) { 30785 return ''; 30786 } 30787 30788 return result[property]; 30789}; 30790 30791/** 30792 * Resuable stable sort function 30793 * 30794 * @param {Playlists} array 30795 * @param {Function}
30795 sortFn Different comparators 30796 * @function stableSort 30797 */ 30798var stableSort = function stableSort(array, sortFn) { 30799 var newArray = array.slice(); 30800 30801 array.sort(function (left, right) { 30802 var cmp = sortFn(left, right); 30803 30804 if (cmp === 0) { 30805 return newArray.indexOf(left) - newArray.indexOf(right); 30806 } 30807 return cmp; 30808 }); 30809}; 30810 30811/** 30812 * A comparator function to sort two playlist object by bandwidth. 30813 * 30814 * @param {Object} left a media playlist object 30815 * @param {Object} right a media playlist object 30816 * @return {Number} Greater than zero if the bandwidth attribute of 30817 * left is greater than the corresponding attribute of right. Less 30818 * than zero if the bandwidth of right is greater than left and 30819 * exactly zero if the two are equal. 30820 */ 30821var comparePlaylistBandwidth = function comparePlaylistBandwidth(left, right) { 30822 var leftBandwidth = undefined; 30823 var rightBandwidth = undefined; 30824 30825 if (left.attributes.BANDWIDTH) { 30826 leftBandwidth = left.attributes.BANDWIDTH; 30827 } 30828 leftBandwidth = leftBandwidth || window.Number.MAX_VALUE; 30829 if (right.attributes.BANDWIDTH) { 30830 rightBandwidth = right.attributes.BANDWIDTH; 30831 } 30832 rightBandwidth = rightBandwidth || window.Number.MAX_VALUE; 30833 30834 return leftBandwidth - rightBandwidth; 30835}; 30836 30837exports.comparePlaylistBandwidth = comparePlaylistBandwidth; 30838/** 30839 * A comparator function to sort two playlist object by resolution (width). 30840 * @param {Object} left a media playlist object 30841 * @param {Object} right a media playlist object 30842 * @return {Number} Greater than zero if the resolution.width attribute of 30843 * left is greater than the corresponding attribute of right. Less 30844 * than zero if the resolution.width of right is greater than left and 30845 * exactly zero if the two are equal. 30846 */ 30847var comparePlaylistResolution = function comparePlaylistResolution(left, right) { 30848 var leftWidth = undefined; 30849 var rightWidth = undefined; 30850 30851 if (left.attributes.RESOLUTION && left.attributes.RESOLUTION.width) { 30852 leftWidth = left.attributes.RESOLUTION.width; 30853 } 30854 30855 leftWidth = leftWidth || window.Number.MAX_VALUE; 30856 30857 if (right.attributes.RESOLUTION && right.attributes.RESOLUTION.width) { 30858 rightWidth = right.attributes.RESOLUTION.width; 30859 } 30860 30861 rightWidth = rightWidth || window.Number.MAX_VALUE; 30862 30863 // NOTE - Fallback to bandwidth sort as appropriate in cases where multiple renditions 30864 // have the same media dimensions/ resolution 30865 if (leftWidth === rightWidth && left.attributes.BANDWIDTH && right.attributes.BANDWIDTH) { 30866 return left.attributes.BANDWIDTH - right.attributes.BANDWIDTH; 30867 } 30868 return leftWidth - rightWidth; 30869}; 30870 30871exports.comparePlaylistResolution = comparePlaylistResolution; 30872/** 30873 * Chooses the appropriate media playlist based on bandwidth and player size 30874 * 30875 * @param {Object} master 30876 * Object representation of the master manifest 30877 * @param {Number} playerBandwidth 30878 * Current calculated bandwidth of the player 30879 * @param {Number} playerWidth 30880 * Current width of the player element 30881 * @param {Number} playerHeight 30882 * Current height of the player element 30883 * @return {Playlist} the highest bitrate playlist less than the 30884 * currently detected bandwidth, accounting for some amount of 30885 * bandwidth variance 30886 */ 30887var simpleSelector = function simpleSelector(master, playerBandwidth, playerWidth, playerHeight) { 30888 // convert the playlists to an intermediary representation to make comparisons easier 30889 var sortedPlaylistReps = master.playlists.map(function (playlist) { 30890 var width = undefined; 30891 var height = undefined; 30892 var bandwidth = undefined; 30893 30894 width = playlist.attributes.RESOLUTION && playlist.attributes.RESOLUTION.width; 30895 height = playlist.attributes.RESOLUTION && playlist.attributes.RESOLUTION.height; 30896 bandwidth = playlist.attributes.BANDWIDTH; 30897 30898 bandwidth = bandwidth || window.Number.MAX_VALUE; 30899 30900 return { 30901 bandwidth: bandwidth, 30902 width: width, 30903 height: height, 30904 playlist: playlist 30905 }; 30906 }); 30907 30908 stableSort(sortedPlaylistReps, function (left, right) { 30909 return left.bandwidth - right.bandwidth; 30910 }); 30911 30912 // filter out any playlists that have been excluded due to 30913 // incompatible configurations or playback errors 30914 sortedPlaylistReps = sortedPlaylistReps.filter(function (rep) { 30915 return _playlist2['default'].isEnabled(rep.playlist); 30916 }); 30917 30918 // filter out any variant that has greater effective bitrate 30919 // than the current estimated bandwidth 30920 var bandwidthPlaylistReps = sortedPlaylistReps.filter(function (rep) { 30921 return rep.bandwidth * _config2['default'].BANDWIDTH_VARIANCE < playerBandwidth; 30922 }); 30923 30924 var highestRemainingBandwidthRep = bandwidthPlaylistReps[bandwidthPlaylistReps.length - 1]; 30925 30926 // get all of the renditions with the same (highest) bandwidth 30927 // and then taking the very first element 30928 var bandwidthBestRep = bandwidthPlaylistReps.filter(function (rep) { 30929 return rep.bandwidth === highestRemainingBandwidthRep.bandwidth; 30930 })[0]; 30931 30932 // filter out playlists without resolution information 30933 var haveResolution = bandwidthPlaylistReps.filter(function (rep) { 30934 return rep.width && rep.height; 30935 }); 30936 30937 // sort variants by resolution 30938 stableSort(haveResolution, function (left, right) { 30939 return left.width - right.width; 30940 }); 30941 30942 // if we have the exact resolution as the player use it 30943 var resolutionBestRepList = haveResolution.filter(function (rep) {
30944 return rep.width === playerWidth && rep.height === playerHeight; 30945 }); 30946 30947 highestRemainingBandwidthRep = resolutionBestRepList[resolutionBestRepList.length - 1]; 30948 // ensure that we pick the highest bandwidth variant that have exact resolution 30949 var resolutionBestRep = resolutionBestRepList.filter(function (rep) { 30950 return rep.bandwidth === highestRemainingBandwidthRep.bandwidth; 30951 })[0]; 30952 30953 var resolutionPlusOneList = undefined; 30954 var resolutionPlusOneSmallest = undefined; 30955 var resolutionPlusOneRep = undefined; 30956 30957 // find the smallest variant that is larger than the player 30958 // if there is no match of exact resolution 30959 if (!resolutionBestRep) { 30960 resolutionPlusOneList = haveResolution.filter(function (rep) { 30961 return rep.width > playerWidth || rep.height > playerHeight; 30962 }); 30963 30964 // find all the variants have the same smallest resolution 30965 resolutionPlusOneSmallest = resolutionPlusOneList.filter(function (rep) { 30966 return rep.width === resolutionPlusOneList[0].width && rep.height === resolutionPlusOneList[0].height; 30967 }); 30968 30969 // ensure that we also pick the highest bandwidth variant that 30970 // is just-larger-than the video player 30971 highestRemainingBandwidthRep = resolutionPlusOneSmallest[resolutionPlusOneSmallest.length - 1]; 30972 resolutionPlusOneRep = resolutionPlusOneSmallest.filter(function (rep) { 30973 return rep.bandwidth === highestRemainingBandwidthRep.bandwidth; 30974 })[0]; 30975 } 30976 30977 // fallback chain of variants 30978 return (resolutionPlusOneRep || resolutionBestRep || bandwidthBestRep || sortedPlaylistReps[0]).playlist; 30979}; 30980 30981exports.simpleSelector = simpleSelector; 30982// Playlist Selectors 30983 30984/** 30985 * Chooses the appropriate media playlist based on the most recent 30986 * bandwidth estimate and the player size. 30987 * 30988 * Expects to be called within the context of an instance of HlsHandler 30989 * 30990 * @return {Playlist} the highest bitrate playlist less than the 30991 * currently detected bandwidth, accounting for some amount of 30992 * bandwidth variance 30993 */ 30994var lastBandwidthSelector = function lastBandwidthSelector() { 30995 return simpleSelector(this.playlists.master, this.systemBandwidth, parseInt(safeGetComputedStyle(this.tech_.el(), 'width'), 10), parseInt(safeGetComputedStyle(this.tech_.el(), 'height'), 10)); 30996}; 30997 30998exports.lastBandwidthSelector = lastBandwidthSelector; 30999/** 31000 * Chooses the appropriate media playlist based on an 31001 * exponential-weighted moving average of the bandwidth after 31002 * filtering for player size. 31003 * 31004 * Expects to be called within the context of an instance of HlsHandler 31005 * 31006 * @param {Number} decay - a number between 0 and 1. Higher values of 31007 * this parameter will cause previous bandwidth estimates to lose 31008 * significance more quickly. 31009 * @return {Function} a function which can be invoked to create a new 31010 * playlist selector function. 31011 * @see https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average 31012 */ 31013var movingAverageBandwidthSelector = function movingAverageBandwidthSelector(decay) { 31014 var average = -1; 31015 31016 if (decay < 0 || decay > 1) { 31017 throw new Error('Moving average bandwidth decay must be between 0 and 1.'); 31018 } 31019 31020 return function () { 31021 if (average < 0) { 31022 average = this.systemBandwidth; 31023 } 31024 31025 average = decay * this.systemBandwidth + (1 - decay) * average; 31026 return simpleSelector(this.playlists.master, average, parseInt(safeGetComputedStyle(this.tech_.el(), 'width'), 10), parseInt(safeGetComputedStyle(this.tech_.el(), 'height'), 10)); 31027 }; 31028}; 31029 31030exports.movingAverageBandwidthSelector = movingAverageBandwidthSelector; 31031/** 31032 * Chooses the appropriate media playlist based on the potential to rebuffer 31033 * 31034 * @param {Object} settings 31035 * Object of information required to use this selector 31036 * @param {Object} settings.master 31037 * Object representation of the master manifest 31038 * @param {Number} settings.currentTime 31039 * The current time of the player 31040 * @param {Number} settings.bandwidth 31041 * Current measured bandwidth 31042 * @param {Number} settings.duration 31043 * Duration of the media 31044 * @param {Number} settings.segmentDuration 31045 * Segment duration to be used in round trip time calculations 31046 * @param {Number} settings.timeUntilRebuffer 31047 * Time left in seconds until the player has to rebuffer 31048 * @param {Number} settings.currentTimeline 31049 * The current timeline segments are being loaded from 31050 * @param {SyncController} settings.syncController 31051 * SyncController for determining if we have a sync point for a given playlist 31052 * @return {Object|null} 31053 * {Object} return.playlist 31054 * The highest bandwidth playlist with the least amount of rebuffering 31055 * {Number} return.rebufferingImpact 31056 * The amount of time in seconds switching to this playlist will rebuffer. A 31057 * negative value means that switching will cause zero rebuffering. 31058 */ 31059var minRebufferMaxBandwidthSelector = function minRebufferMaxBandwidthSelector(settings) { 31060 var master = settings.master; 31061 var currentTime = settings.currentTime; 31062 var bandwidth = settings.bandwidth; 31063 var duration = settings.duration; 31064 var segmentDuration = settings.segmentDuration; 31065 var timeUntilRebuffer = settings.timeUntilRebuffer; 31066 var currentTimeline = settings.currentTimeline; 31067 var syncController = settings.syncController; 31068 31069 var bandwidthPlaylists = master.playlists.filter(function (playlist) { 31070 return _playlist2['default'].isEnabled(playlist) && _playlist2['default'].hasAttribute('BANDWIDTH', playlist); 31071 }); 31072 31073 var rebufferingEstimates = bandwidthPlaylists.map(function (playlist) { 31074 var syncPoint = syncController.getSyncPoint(playlist, duration, currentTimeline, currentTime); 31075 // If there is no sync point for this playlist, switching to it will require a 31076 // sync request first. This will double the request time 31077 var numRequests = syncPoint ? 1 : 2; 31078 var requestTimeEstimate = _playlist2['default'].estimateSegmentRequestTime(segmentDuration, bandwidth, playlist); 31079 var rebufferingImpact = requestTimeEstimate * numRequests - timeUntilRebuffer; 31080 31081 return { 31082 playlist: playlist, 31083 rebufferingImpact: rebufferingImpact 31084 }; 31085 }); 31086 31087 var noRebufferingPlaylists = rebufferingEstimates.filter(function (estimate) { 31088 return estimate.rebufferingImpact <= 0; 31089 }); 31090 31091 // Sort by bandwidth DESC 31092 stableSort(noRebufferingPlaylists, function (a, b) { 31093 return comparePlaylistBandwidth(b.playlist, a.playlist); 31094 }); 31095 31096 if (noRebufferingPlaylists.length) { 31097 return noRebufferingPlaylists[0]; 31098 } 31099 31100 stableSort(rebufferingEstimates, function (a, b) { 31101 return a.rebufferingImpact - b.rebufferingImpact; 31102 }); 31103 31104 return rebufferingEstimates[0] || null; 31105}; 31106 31107exports.minRebufferMaxBandwidthSelector = minRebufferMaxBandwidthSelector; 31108/** 31109 * Chooses the appropriate media playlist, which in this case is the lowest bitrate 31110 * one with video. If no renditions with video exist, return the lowest audio rendition. 31111 * 31112 * Expects to be called within the context of an instance of HlsHandler 31113 * 31114 * @return {Object|null} 31115 * {Object} return.playlist 31116 * The lowest bitrate playlist that contains a video codec. If no such rendition 31117 * exists pick the lowest audio rendition. 31118 */ 31119var lowestBitrateCompatibleVariantSelector = function lowestBitrateCompatibleVariantSelector() {
31120 // filter out any playlists that have been excluded due to 31121 // incompatible configurations or playback errors 31122 var playlists = this.playlists.master.playlists.filter(_playlist2['default'].isEnabled); 31123 31124 // Sort ascending by bitrate 31125 stableSort(playlists, function (a, b) { 31126 return comparePlaylistBandwidth(a, b); 31127 }); 31128 31129 // Parse and assume that playlists with no video codec have no video 31130 // (this is not necessarily true, although it is generally true). 31131 // 31132 // If an entire manifest has no valid videos everything will get filtered 31133 // out. 31134 var playlistsWithVideo = playlists.filter(function (playlist) { 31135 return (0, _utilCodecsJs.parseCodecs)(playlist.attributes.CODECS).videoCodec; 31136 }); 31137 31138 return playlistsWithVideo[0] || null; 31139}; 31140exports.lowestBitrateCompatibleVariantSelector = lowestBitrateCompatibleVariantSelector; 31141},{"./config":3,"./playlist":11,"./util/codecs.js":19}],11:[function(require,module,exports){ 31142(function (global){ 31143/** 31144 * @file playlist.js 31145 * 31146 * Playlist related utilities. 31147 */ 31148'use strict'; 31149 31150Object.defineProperty(exports, '__esModule', { 31151 value: true 31152}); 31153 31154function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 31155 31156var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null); 31157 31158var _globalWindow = require('global/window'); 31159 31160var _globalWindow2 = _interopRequireDefault(_globalWindow); 31161 31162var Playlist = { 31163 /** 31164 * The number of segments that are unsafe to start playback at in 31165 * a live stream. Changing this value can cause playback stalls. 31166 * See HTTP Live Streaming, "Playing the Media Playlist File" 31167 * https://tools.ietf.org/html/draft-pantos-http-live-streaming-18#section-6.3.3 31168 */ 31169 UNSAFE_LIVE_SEGMENTS: 3 31170}; 31171 31172/** 31173 * walk backward until we find a duration we can use 31174 * or return a failure 31175 * 31176 * @param {Playlist} playlist the playlist to walk through 31177 * @param {Number} endSequence the mediaSequence to stop walking on 31178 */ 31179 31180var backwardDuration = function backwardDuration(playlist, endSequence) { 31181 var result = 0; 31182 var i = endSequence - playlist.mediaSequence; 31183 // if a start time is available for segment immediately following 31184 // the interval, use it 31185 var segment = playlist.segments[i]; 31186 31187 // Walk backward until we find the latest segment with timeline 31188 // information that is earlier than endSequence 31189 if (segment) { 31190 if (typeof segment.start !== 'undefined') { 31191 return { result: segment.start, precise: true }; 31192 } 31193 if (typeof segment.end !== 'undefined') { 31194 return { 31195 result: segment.end - segment.duration, 31196 precise: true 31197 }; 31198 } 31199 } 31200 while (i--) { 31201 segment = playlist.segments[i]; 31202 if (typeof segment.end !== 'undefined') { 31203 return { result: result + segment.end, precise: true }; 31204 } 31205 31206 result += segment.duration; 31207 31208 if (typeof segment.start !== 'undefined') { 31209 return { result: result + segment.start, precise: true }; 31210 } 31211 } 31212 return { result: result, precise: false }; 31213}; 31214 31215/** 31216 * walk forward until we find a duration we can use 31217 * or return a failure 31218 * 31219 * @param {Playlist} playlist the playlist to walk through 31220 * @param {Number} endSequence the mediaSequence to stop walking on 31221 */ 31222var forwardDuration = function forwardDuration(playlist, endSequence) { 31223 var result = 0; 31224 var segment = undefined; 31225 var i = endSequence - playlist.mediaSequence; 31226 // Walk forward until we find the earliest segment with timeline 31227 // information 31228 31229 for (; i < playlist.segments.length; i++) { 31230 segment = playlist.segments[i]; 31231 if (typeof segment.start !== 'undefined') { 31232 return { 31233 result: segment.start - result, 31234 precise: true 31235 }; 31236 } 31237 31238 result += segment.duration; 31239 31240 if (typeof segment.end !== 'undefined') { 31241 return { 31242 result: segment.end - result, 31243 precise: true 31244 }; 31245 } 31246 } 31247 // indicate we didn't find a useful duration estimate 31248 return { result: -1, precise: false }; 31249}; 31250 31251/** 31252 * Calculate the media duration from the segments associated with a 31253 * playlist. The duration of a subinterval of the available segments 31254 * may be calculated by specifying an end index. 31255 * 31256 * @param {Object} playlist a media playlist object 31257 * @param {Number=} endSequence an exclusive upper boundary 31258 * for the playlist. Defaults to playlist length. 31259 * @param {Number} expired the amount of time that has dropped 31260 * off the front of the playlist in a live scenario 31261 * @return {Number} the duration between the first available segment 31262 * and end index. 31263 */ 31264var intervalDuration = function intervalDuration(playlist, endSequence, expired) { 31265 var backward = undefined; 31266 var forward = undefined; 31267 31268 if (typeof endSequence === 'undefined') { 31269 endSequence = playlist.mediaSequence + playlist.segments.length; 31270 } 31271 31272 if (endSequence < playlist.mediaSequence) { 31273 return 0; 31274 } 31275
31276 // do a backward walk to estimate the duration 31277 backward = backwardDuration(playlist, endSequence); 31278 if (backward.precise) { 31279 // if we were able to base our duration estimate on timing 31280 // information provided directly from the Media Source, return 31281 // it 31282 return backward.result; 31283 } 31284 31285 // walk forward to see if a precise duration estimate can be made 31286 // that way 31287 forward = forwardDuration(playlist, endSequence); 31288 if (forward.precise) { 31289 // we found a segment that has been buffered and so it's 31290 // position is known precisely 31291 return forward.result; 31292 } 31293 31294 // return the less-precise, playlist-based duration estimate 31295 return backward.result + expired; 31296}; 31297 31298/** 31299 * Calculates the duration of a playlist. If a start and end index 31300 * are specified, the duration will be for the subset of the media 31301 * timeline between those two indices. The total duration for live 31302 * playlists is always Infinity. 31303 * 31304 * @param {Object} playlist a media playlist object 31305 * @param {Number=} endSequence an exclusive upper 31306 * boundary for the playlist. Defaults to the playlist media 31307 * sequence number plus its length. 31308 * @param {Number=} expired the amount of time that has 31309 * dropped off the front of the playlist in a live scenario 31310 * @return {Number} the duration between the start index and end 31311 * index. 31312 */ 31313var duration = function duration(playlist, endSequence, expired) { 31314 if (!playlist) { 31315 return 0; 31316 } 31317 31318 if (typeof expired !== 'number') { 31319 expired = 0; 31320 } 31321 31322 // if a slice of the total duration is not requested, use 31323 // playlist-level duration indicators when they're present 31324 if (typeof endSequence === 'undefined') { 31325 // if present, use the duration specified in the playlist 31326 if (playlist.totalDuration) { 31327 return playlist.totalDuration; 31328 } 31329 31330 // duration should be Infinity for live playlists 31331 if (!playlist.endList) { 31332 return _globalWindow2['default'].Infinity; 31333 } 31334 } 31335 31336 // calculate the total duration based on the segment durations 31337 return intervalDuration(playlist, endSequence, expired); 31338}; 31339 31340exports.duration = duration; 31341/** 31342 * Calculate the time between two indexes in the current playlist 31343 * neight the start- nor the end-index need to be within the current 31344 * playlist in which case, the targetDuration of the playlist is used 31345 * to approximate the durations of the segments 31346 * 31347 * @param {Object} playlist a media playlist object 31348 * @param {Number} startIndex 31349 * @param {Number} endIndex 31350 * @return {Number} the number of seconds between startIndex and endIndex 31351 */ 31352var sumDurations = function sumDurations(playlist, startIndex, endIndex) { 31353 var durations = 0; 31354 31355 if (startIndex > endIndex) { 31356 var _ref = [endIndex, startIndex]; 31357 startIndex = _ref[0]; 31358 endIndex = _ref[1]; 31359 } 31360 31361 if (startIndex < 0) { 31362 for (var i = startIndex; i < Math.min(0, endIndex); i++) { 31363 durations += playlist.targetDuration; 31364 } 31365 startIndex = 0; 31366 } 31367 31368 for (var i = startIndex; i < endIndex; i++) { 31369 durations += playlist.segments[i].duration; 31370 } 31371 31372 return durations; 31373}; 31374 31375exports.sumDurations = sumDurations; 31376/** 31377 * Calculates the playlist end time 31378 * 31379 * @param {Object} playlist a media playlist object 31380 * @param {Number=} expired the amount of time that has 31381 * dropped off the front of the playlist in a live scenario 31382 * @param {Boolean|false} useSafeLiveEnd a boolean value indicating whether or not the 31383 * playlist end calculation should consider the safe live end 31384 * (truncate the playlist end by three segments). This is normally 31385 * used for calculating the end of the playlist's seekable range. 31386 * @returns {Number} the end time of playlist 31387 * @function playlistEnd 31388 */ 31389var playlistEnd = function playlistEnd(playlist, expired, useSafeLiveEnd) { 31390 if (!playlist || !playlist.segments) { 31391 return null; 31392 } 31393 if (playlist.endList) { 31394 return duration(playlist); 31395 } 31396 31397 if (expired === null) { 31398 return null; 31399 } 31400 31401 expired = expired || 0; 31402 31403 var endSequence = useSafeLiveEnd ? Math.max(0, playlist.segments.length - Playlist.UNSAFE_LIVE_SEGMENTS) : Math.max(0, playlist.segments.length); 31404 31405 return intervalDuration(playlist, playlist.mediaSequence + endSequence, expired); 31406}; 31407 31408exports.playlistEnd = playlistEnd; 31409/** 31410 * Calculates the interval of time that is currently seekable in a 31411 * playlist. The returned time ranges are relative to the earliest 31412 * moment in the specified playlist that is still available. A full 31413 * seekable implementation for live streams would need to offset 31414 * these values by the duration of content that has expired from the 31415 * stream. 31416 * 31417 * @param {Object} playlist a media playlist object 31418 * dropped off the front of the playlist in a live scenario 31419 * @param {Number=} expired the amount of time that has 31420 * dropped off the front of the playlist in a live scenario 31421 * @return {TimeRanges}
vendor: 4,595 bytes, lines 31421-31576
31421 the periods of time that are valid targets 31422 * for seeking 31423 */ 31424var seekable = function seekable(playlist, expired) { 31425 var useSafeLiveEnd = true; 31426 var seekableStart = expired || 0; 31427 var seekableEnd = playlistEnd(playlist, expired, useSafeLiveEnd); 31428 31429 if (seekableEnd === null) { 31430 return (0, _videoJs.createTimeRange)(); 31431 } 31432 return (0, _videoJs.createTimeRange)(seekableStart, seekableEnd); 31433}; 31434 31435exports.seekable = seekable; 31436var isWholeNumber = function isWholeNumber(num) { 31437 return num - Math.floor(num) === 0; 31438}; 31439 31440var roundSignificantDigit = function roundSignificantDigit(increment, num) { 31441 // If we have a whole number, just add 1 to it 31442 if (isWholeNumber(num)) { 31443 return num + increment * 0.1; 31444 } 31445 31446 var numDecimalDigits = num.toString().split('.')[1].length; 31447 31448 for (var i = 1; i <= numDecimalDigits; i++) { 31449 var scale = Math.pow(10, i); 31450 var temp = num * scale; 31451 31452 if (isWholeNumber(temp) || i === numDecimalDigits) { 31453 return (temp + increment) / scale; 31454 } 31455 } 31456}; 31457 31458var ceilLeastSignificantDigit = roundSignificantDigit.bind(null, 1); 31459var floorLeastSignificantDigit = roundSignificantDigit.bind(null, -1); 31460 31461/** 31462 * Determine the index and estimated starting time of the segment that 31463 * contains a specified playback position in a media playlist. 31464 * 31465 * @param {Object} playlist the media playlist to query 31466 * @param {Number} currentTime The number of seconds since the earliest 31467 * possible position to determine the containing segment for 31468 * @param {Number} startIndex 31469 * @param {Number} startTime 31470 * @return {Object} 31471 */ 31472var getMediaInfoForTime = function getMediaInfoForTime(playlist, currentTime, startIndex, startTime) { 31473 var i = undefined; 31474 var segment = undefined; 31475 var numSegments = playlist.segments.length; 31476 31477 var time = currentTime - startTime; 31478 31479 if (time < 0) { 31480 // Walk backward from startIndex in the playlist, adding durations 31481 // until we find a segment that contains `time` and return it 31482 if (startIndex > 0) { 31483 for (i = startIndex - 1; i >= 0; i--) { 31484 segment = playlist.segments[i]; 31485 time += floorLeastSignificantDigit(segment.duration); 31486 if (time > 0) { 31487 return { 31488 mediaIndex: i, 31489 startTime: startTime - sumDurations(playlist, startIndex, i) 31490 }; 31491 } 31492 } 31493 } 31494 // We were unable to find a good segment within the playlist 31495 // so select the first segment 31496 return { 31497 mediaIndex: 0, 31498 startTime: currentTime 31499 }; 31500 } 31501 31502 // When startIndex is negative, we first walk forward to first segment 31503 // adding target durations. If we "run out of time" before getting to 31504 // the first segment, return the first segment 31505 if (startIndex < 0) { 31506 for (i = startIndex; i < 0; i++) { 31507 time -= playlist.targetDuration; 31508 if (time < 0) { 31509 return { 31510 mediaIndex: 0, 31511 startTime: currentTime 31512 }; 31513 } 31514 } 31515 startIndex = 0; 31516 } 31517 31518 // Walk forward from startIndex in the playlist, subtracting durations 31519 // until we find a segment that contains `time` and return it 31520 for (i = startIndex; i < numSegments; i++) { 31521 segment = playlist.segments[i]; 31522 time -= ceilLeastSignificantDigit(segment.duration); 31523 if (time < 0) { 31524 return { 31525 mediaIndex: i, 31526 startTime: startTime + sumDurations(playlist, startIndex, i) 31527 }; 31528 } 31529 } 31530 31531 // We are out of possible candidates so load the last one... 31532 return { 31533 mediaIndex: numSegments - 1, 31534 startTime: currentTime 31535 }; 31536}; 31537 31538exports.getMediaInfoForTime = getMediaInfoForTime; 31539/** 31540 * Check whether the playlist is blacklisted or not. 31541 * 31542 * @param {Object} playlist the media playlist object 31543 * @return {boolean} whether the playlist is blacklisted or not 31544 * @function isBlacklisted 31545 */ 31546var isBlacklisted = function isBlacklisted(playlist) { 31547 return playlist.excludeUntil && playlist.excludeUntil > Date.now(); 31548}; 31549 31550exports.isBlacklisted = isBlacklisted; 31551/** 31552 * Check whether the playlist is enabled or not. 31553 * 31554 * @param {Object} playlist the media playlist object 31555 * @return {boolean} whether the playlist is enabled or not 31556 * @function isEnabled 31557 */ 31558var isEnabled = function isEnabled(playlist) { 31559 var blacklisted = isBlacklisted(playlist); 31560 31561 return !playlist.disabled && !blacklisted; 31562}; 31563 31564exports.isEnabled = isEnabled; 31565/** 31566 * Returns whether the current playlist is an AES encrypted HLS stream 31567 * 31568 * @return {Boolean} true if it's an AES encrypted HLS stream 31569 */ 31570var isAes = function isAes(media) { 31571 for (var i = 0; i < media.segments.length; i++) { 31572 if (media.segments[i].key) { 31573 return true; 31574 } 31575 } 31576 return false;
31577}; 31578 31579exports.isAes = isAes; 31580/** 31581 * Returns whether the current playlist contains fMP4 31582 * 31583 * @return {Boolean} true if the playlist contains fMP4 31584 */ 31585var isFmp4 = function isFmp4(media) { 31586 for (var i = 0; i < media.segments.length; i++) { 31587 if (media.segments[i].map) { 31588 return true; 31589 } 31590 } 31591 return false; 31592}; 31593 31594exports.isFmp4 = isFmp4; 31595/** 31596 * Checks if the playlist has a value for the specified attribute 31597 * 31598 * @param {String} attr 31599 * Attribute to check for 31600 * @param {Object} playlist 31601 * The media playlist object 31602 * @return {Boolean} 31603 * Whether the playlist contains a value for the attribute or not 31604 * @function hasAttribute 31605 */ 31606var hasAttribute = function hasAttribute(attr, playlist) { 31607 return playlist.attributes && playlist.attributes[attr]; 31608}; 31609 31610exports.hasAttribute = hasAttribute; 31611/** 31612 * Estimates the time required to complete a segment download from the specified playlist 31613 * 31614 * @param {Number} segmentDuration 31615 * Duration of requested segment 31616 * @param {Number} bandwidth 31617 * Current measured bandwidth of the player 31618 * @param {Object} playlist 31619 * The media playlist object 31620 * @param {Number=} bytesReceived 31621 * Number of bytes already received for the request. Defaults to 0 31622 * @return {Number|NaN} 31623 * The estimated time to request the segment. NaN if bandwidth information for 31624 * the given playlist is unavailable 31625 * @function estimateSegmentRequestTime 31626 */ 31627var estimateSegmentRequestTime = function estimateSegmentRequestTime(segmentDuration, bandwidth, playlist) { 31628 var bytesReceived = arguments.length <= 3 || arguments[3] === undefined ? 0 : arguments[3]; 31629 31630 if (!hasAttribute('BANDWIDTH', playlist)) { 31631 return NaN; 31632 } 31633 31634 var size = segmentDuration * playlist.attributes.BANDWIDTH; 31635 31636 return (size - bytesReceived * 8) / bandwidth; 31637}; 31638 31639exports.estimateSegmentRequestTime = estimateSegmentRequestTime; 31640Playlist.duration = duration; 31641Playlist.seekable = seekable; 31642Playlist.getMediaInfoForTime = getMediaInfoForTime; 31643Playlist.isEnabled = isEnabled; 31644Playlist.isBlacklisted = isBlacklisted; 31645Playlist.playlistEnd = playlistEnd; 31646Playlist.isAes = isAes; 31647Playlist.isFmp4 = isFmp4; 31648Playlist.hasAttribute = hasAttribute; 31649Playlist.estimateSegmentRequestTime = estimateSegmentRequestTime; 31650 31651// exports 31652exports['default'] = Playlist; 31653}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 31654},{"global/window":32}],12:[function(require,module,exports){ 31655(function (global){ 31656/** 31657 * ranges 31658 * 31659 * Utilities for working with TimeRanges. 31660 * 31661 */ 31662 31663'use strict'; 31664 31665Object.defineProperty(exports, '__esModule', { 31666 value: true 31667}); 31668 31669var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })(); 31670 31671function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 31672 31673var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null); 31674 31675var _videoJs2 = _interopRequireDefault(_videoJs); 31676 31677// Fudge factor to account for TimeRanges rounding 31678var TIME_FUDGE_FACTOR = 1 / 30; 31679 31680/** 31681 * Clamps a value to within a range 31682 * @param {Number} num - the value to clamp 31683 * @param {Number} start - the start of the range to clamp within, inclusive 31684 * @param {Number} end - the end of the range to clamp within, inclusive 31685 * @return {Number} 31686 */ 31687var clamp = function clamp(num, _ref) { 31688 var _ref2 = _slicedToArray(_ref, 2); 31689 31690 var start = _ref2[0]; 31691 var end = _ref2[1]; 31692 31693 return Math.min(Math.max(start, num), end); 31694}; 31695var filterRanges = function filterRanges(timeRanges, predicate) { 31696 var results = []; 31697 var i = undefined; 31698 31699 if (timeRanges && timeRanges.length) { 31700 // Search for ranges that match the predicate 31701 for (i = 0; i < timeRanges.length; i++) { 31702 if (predicate(timeRanges.start(i), timeRanges.end(i))) { 31703 results.push([timeRanges.start(i), timeRanges.end(i)]); 31704 } 31705 } 31706 } 31707 31708 return _videoJs2['default'].createTimeRanges(results); 31709}; 31710 31711/** 31712 * Attempts to find the buffered TimeRange that contains the specified 31713 * time. 31714 * @param {TimeRanges} buffered - the TimeRanges object to query 31715 * @param {number} time - the time to filter on. 31716 * @returns {TimeRanges} a new TimeRanges object 31717 */ 31718var findRange = function findRange(buffered, time) { 31719 return filterRanges(buffered, function (start, end) {
31720 return start - TIME_FUDGE_FACTOR <= time && end + TIME_FUDGE_FACTOR >= time; 31721 }); 31722}; 31723 31724/** 31725 * Returns the TimeRanges that begin later than the specified time. 31726 * @param {TimeRanges} timeRanges - the TimeRanges object to query 31727 * @param {number} time - the time to filter on. 31728 * @returns {TimeRanges} a new TimeRanges object. 31729 */ 31730var findNextRange = function findNextRange(timeRanges, time) { 31731 return filterRanges(timeRanges, function (start) { 31732 return start - TIME_FUDGE_FACTOR >= time; 31733 }); 31734}; 31735 31736/** 31737 * Returns gaps within a list of TimeRanges 31738 * @param {TimeRanges} buffered - the TimeRanges object 31739 * @return {TimeRanges} a TimeRanges object of gaps 31740 */ 31741var findGaps = function findGaps(buffered) { 31742 if (buffered.length < 2) { 31743 return _videoJs2['default'].createTimeRanges(); 31744 } 31745 31746 var ranges = []; 31747 31748 for (var i = 1; i < buffered.length; i++) { 31749 var start = buffered.end(i - 1); 31750 var end = buffered.start(i); 31751 31752 ranges.push([start, end]); 31753 } 31754 31755 return _videoJs2['default'].createTimeRanges(ranges); 31756}; 31757 31758/** 31759 * Search for a likely end time for the segment that was just appened 31760 * based on the state of the `buffered` property before and after the 31761 * append. If we fin only one such uncommon end-point return it. 31762 * @param {TimeRanges} original - the buffered time ranges before the update 31763 * @param {TimeRanges} update - the buffered time ranges after the update 31764 * @returns {Number|null} the end time added between `original` and `update`, 31765 * or null if one cannot be unambiguously determined. 31766 */ 31767var findSoleUncommonTimeRangesEnd = function findSoleUncommonTimeRangesEnd(original, update) { 31768 var i = undefined; 31769 var start = undefined; 31770 var end = undefined; 31771 var result = []; 31772 var edges = []; 31773 31774 // In order to qualify as a possible candidate, the end point must: 31775 // 1) Not have already existed in the `original` ranges 31776 // 2) Not result from the shrinking of a range that already existed 31777 // in the `original` ranges 31778 // 3) Not be contained inside of a range that existed in `original` 31779 var overlapsCurrentEnd = function overlapsCurrentEnd(span) { 31780 return span[0] <= end && span[1] >= end; 31781 }; 31782 31783 if (original) { 31784 // Save all the edges in the `original` TimeRanges object 31785 for (i = 0; i < original.length; i++) { 31786 start = original.start(i); 31787 end = original.end(i); 31788 31789 edges.push([start, end]); 31790 } 31791 } 31792 31793 if (update) { 31794 // Save any end-points in `update` that are not in the `original` 31795 // TimeRanges object 31796 for (i = 0; i < update.length; i++) { 31797 start = update.start(i); 31798 end = update.end(i); 31799 31800 if (edges.some(overlapsCurrentEnd)) { 31801 continue; 31802 } 31803 31804 // at this point it must be a unique non-shrinking end edge 31805 result.push(end); 31806 } 31807 } 31808 31809 // we err on the side of caution and return null if didn't find 31810 // exactly *one* differing end edge in the search above 31811 if (result.length !== 1) { 31812 return null; 31813 } 31814 31815 return result[0]; 31816}; 31817 31818/** 31819 * Calculate the intersection of two TimeRanges 31820 * @param {TimeRanges} bufferA 31821 * @param {TimeRanges} bufferB 31822 * @returns {TimeRanges} The interesection of `bufferA` with `bufferB` 31823 */ 31824var bufferIntersection = function bufferIntersection(bufferA, bufferB) { 31825 var start = null; 31826 var end = null; 31827 var arity = 0; 31828 var extents = []; 31829 var ranges = []; 31830 31831 if (!bufferA || !bufferA.length || !bufferB || !bufferB.length) { 31832 return _videoJs2['default'].createTimeRange(); 31833 } 31834 31835 // Handle the case where we have both buffers and create an 31836 // intersection of the two 31837 var count = bufferA.length; 31838 31839 // A) Gather up all start and end times 31840 while (count--) { 31841 extents.push({ time: bufferA.start(count), type: 'start' }); 31842 extents.push({ time: bufferA.end(count), type: 'end' }); 31843 } 31844 count = bufferB.length; 31845 while (count--) { 31846 extents.push({ time: bufferB.start(count), type: 'start' }); 31847 extents.push({ time: bufferB.end(count), type: 'end' }); 31848 } 31849 // B) Sort them by time 31850 extents.sort(function (a, b) { 31851 return a.time - b.time; 31852 }); 31853 31854 // C) Go along one by one incrementing arity for start and decrementing 31855 // arity for ends 31856 for (count = 0; count < extents.length; count++) { 31857 if (extents[count].type === 'start') { 31858 arity++; 31859 31860 // D) If arity is ever incremented to 2 we are entering an 31861 // overlapping range 31862 if (arity === 2) { 31863 start = extents[count].time; 31864 } 31865 } else if (extents[count].type === 'end') { 31866 arity--; 31867
31868 // E) If arity is ever decremented to 1 we leaving an 31869 // overlapping range 31870 if (arity === 1) { 31871 end = extents[count].time; 31872 } 31873 } 31874 31875 // F) Record overlapping ranges 31876 if (start !== null && end !== null) { 31877 ranges.push([start, end]); 31878 start = null; 31879 end = null; 31880 } 31881 } 31882 31883 return _videoJs2['default'].createTimeRanges(ranges); 31884}; 31885 31886/** 31887 * Calculates the percentage of `segmentRange` that overlaps the 31888 * `buffered` time ranges. 31889 * @param {TimeRanges} segmentRange - the time range that the segment 31890 * covers adjusted according to currentTime 31891 * @param {TimeRanges} referenceRange - the original time range that the 31892 * segment covers 31893 * @param {Number} currentTime - time in seconds where the current playback 31894 * is at 31895 * @param {TimeRanges} buffered - the currently buffered time ranges 31896 * @returns {Number} percent of the segment currently buffered 31897 */ 31898var calculateBufferedPercent = function calculateBufferedPercent(adjustedRange, referenceRange, currentTime, buffered) { 31899 var referenceDuration = referenceRange.end(0) - referenceRange.start(0); 31900 var adjustedDuration = adjustedRange.end(0) - adjustedRange.start(0); 31901 var bufferMissingFromAdjusted = referenceDuration - adjustedDuration; 31902 var adjustedIntersection = bufferIntersection(adjustedRange, buffered); 31903 var referenceIntersection = bufferIntersection(referenceRange, buffered); 31904 var adjustedOverlap = 0; 31905 var referenceOverlap = 0; 31906 31907 var count = adjustedIntersection.length; 31908 31909 while (count--) { 31910 adjustedOverlap += adjustedIntersection.end(count) - adjustedIntersection.start(count); 31911 31912 // If the current overlap segment starts at currentTime, then increase the 31913 // overlap duration so that it actually starts at the beginning of referenceRange 31914 // by including the difference between the two Range's durations 31915 // This is a work around for the way Flash has no buffer before currentTime 31916 if (adjustedIntersection.start(count) === currentTime) { 31917 adjustedOverlap += bufferMissingFromAdjusted; 31918 } 31919 } 31920 31921 count = referenceIntersection.length; 31922 31923 while (count--) { 31924 referenceOverlap += referenceIntersection.end(count) - referenceIntersection.start(count); 31925 } 31926 31927 // Use whichever value is larger for the percentage-buffered since that value 31928 // is likely more accurate because the only way 31929 return Math.max(adjustedOverlap, referenceOverlap) / referenceDuration * 100; 31930}; 31931 31932/** 31933 * Return the amount of a range specified by the startOfSegment and segmentDuration 31934 * overlaps the current buffered content. 31935 * 31936 * @param {Number} startOfSegment - the time where the segment begins 31937 * @param {Number} segmentDuration - the duration of the segment in seconds 31938 * @param {Number} currentTime - time in seconds where the current playback 31939 * is at 31940 * @param {TimeRanges} buffered - the state of the buffer 31941 * @returns {Number} percentage of the segment's time range that is 31942 * already in `buffered` 31943 */ 31944var getSegmentBufferedPercent = function getSegmentBufferedPercent(startOfSegment, segmentDuration, currentTime, buffered) { 31945 var endOfSegment = startOfSegment + segmentDuration; 31946 31947 // The entire time range of the segment 31948 var originalSegmentRange = _videoJs2['default'].createTimeRanges([[startOfSegment, endOfSegment]]); 31949 31950 // The adjusted segment time range that is setup such that it starts 31951 // no earlier than currentTime 31952 // Flash has no notion of a back-buffer so adjustedSegmentRange adjusts 31953 // for that and the function will still return 100% if a only half of a 31954 // segment is actually in the buffer as long as the currentTime is also 31955 // half-way through the segment 31956 var adjustedSegmentRange = _videoJs2['default'].createTimeRanges([[clamp(startOfSegment, [currentTime, endOfSegment]), endOfSegment]]); 31957 31958 // This condition happens when the currentTime is beyond the segment's 31959 // end time 31960 if (adjustedSegmentRange.start(0) === adjustedSegmentRange.end(0)) { 31961 return 0; 31962 } 31963 31964 var percent = calculateBufferedPercent(adjustedSegmentRange, originalSegmentRange, currentTime, buffered); 31965 31966 // If the segment is reported as having a zero duration, return 0% 31967 // since it is likely that we will need to fetch the segment 31968 if (isNaN(percent) || percent === Infinity || percent === -Infinity) { 31969 return 0; 31970 } 31971 31972 return percent; 31973}; 31974 31975/** 31976 * Gets a human readable string for a TimeRange 31977 * 31978 * @param {TimeRange} range 31979 * @returns {String} a human readable string 31980 */ 31981var printableRange = function printableRange(range) { 31982 var strArr = []; 31983 31984 if (!range || !range.length) { 31985 return ''; 31986 } 31987 31988 for (var i = 0; i < range.length; i++) { 31989 strArr.push(range.start(i) + ' => ' + range.end(i)); 31990 } 31991 31992 return strArr.join(', '); 31993}; 31994 31995/** 31996 * Calculates the amount of time left in seconds until the player hits the end of the 31997 * buffer and causes a rebuffer 31998 * 31999 * @param {TimeRange} buffered 32000 * The state of the buffer 32001 * @param {Numnber} currentTime 32002 * The current time of the player 32003 * @param {Number} playbackRate 32004 * The current playback rate of the player. Defaults to 1. 32005 * @return {Number} 32006 * Time until the player has to start rebuffering in seconds. 32007 * @function timeUntilRebuffer 32008 */ 32009var timeUntilRebuffer = function timeUntilRebuffer(buffered, currentTime) { 32010 var playbackRate = arguments.length <= 2 || arguments[2] === undefined ? 1 : arguments[2]; 32011 32012 var bufferedEnd = buffered.length ? buffered.end(buffered.length - 1) : 0; 32013 32014 return (bufferedEnd - currentTime) / playbackRate; 32015}; 32016 32017exports['default'] = { 32018 findRange: findRange, 32019 findNextRange: findNextRange, 32020 findGaps: findGaps, 32021 findSoleUncommonTimeRangesEnd: findSoleUncommonTimeRangesEnd, 32022 getSegmentBufferedPercent: getSegmentBufferedPercent, 32023 TIME_FUDGE_FACTOR: TIME_FUDGE_FACTOR, 32024 printableRange: printableRange, 32025 timeUntilRebuffer: timeUntilRebuffer 32026}; 32027module.exports = exports['default']; 32028}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 32029},{}],13:[function(require,module,exports){ 32030(function (global){ 32031'use strict'; 32032 32033Object.defineProperty(exports, '__esModule', { 32034 value: true 32035}); 32036 32037function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 32038 32039var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null); 32040 32041var _videoJs2 = _interopRequireDefault(_videoJs); 32042 32043var defaultOptions = { 32044 errorInterval: 30, 32045 getSource: function getSource(next) { 32046 var tech = this.tech({ IWillNotUseThisInPlugins: true }); 32047 var sourceObj = tech.currentSource_; 32048 32049 return next(sourceObj); 32050 } 32051}; 32052 32053/** 32054 * Main entry point for the plugin 32055 * 32056 * @param {Player} player a reference to a videojs Player instance 32057 * @param {Object} [options] an object with plugin options 32058 * @private 32059 */ 32060var initPlugin = function initPlugin(player, options) { 32061 var lastCalled = 0; 32062 var seekTo = 0; 32063 var localOptions = _videoJs2['default'].mergeOptions(defaultOptions, options); 32064 32065 player.ready(function () { 32066 player.trigger({ type: 'usage', name: 'hls-error-reload-initialized' }); 32067 }); 32068 32069 /** 32070 * Player modifications to perform that must wait until `loadedmetadata` 32071 * has been triggered 32072 * 32073 * @private 32074 */ 32075 var loadedMetadataHandler = function loadedMetadataHandler() { 32076 if (seekTo) { 32077 player.currentTime(seekTo); 32078 } 32079 }; 32080 32081 /** 32082 * Set the source on the player element, play, and seek if necessary 32083 * 32084 * @param {Object} sourceObj An object specifying the source url and mime-type to play 32085 * @private 32086 */ 32087 var setSource = function setSource(sourceObj) { 32088 if (sourceObj === null || sourceObj === undefined) { 32089 return; 32090 } 32091 seekTo = player.duration() !== Infinity && player.currentTime() || 0; 32092 32093 player.one('loadedmetadata', loadedMetadataHandler); 32094 32095 player.src(sourceObj); 32096 player.trigger({ type: 'usage', name: 'hls-error-reload' }); 32097 player.play(); 32098 }; 32099 32100 /** 32101 * Attempt to get a source from either the built-in getSource function 32102 * or a custom function provided via the options 32103 * 32104 * @private 32105 */ 32106 var errorHandler = function errorHandler() {
32107 // Do not attempt to reload the source if a source-reload occurred before 32108 // 'errorInterval' time has elapsed since the last source-reload 32109 if (Date.now() - lastCalled < localOptions.errorInterval * 1000) { 32110 player.trigger({ type: 'usage', name: 'hls-error-reload-canceled' }); 32111 return; 32112 } 32113 32114 if (!localOptions.getSource || typeof localOptions.getSource !== 'function') { 32115 _videoJs2['default'].log.error('ERROR: reloadSourceOnError - The option getSource must be a function!'); 32116 return; 32117 } 32118 lastCalled = Date.now(); 32119 32120 return localOptions.getSource.call(player, setSource); 32121 }; 32122 32123 /** 32124 * Unbind any event handlers that were bound by the plugin 32125 * 32126 * @private 32127 */ 32128 var cleanupEvents = function cleanupEvents() { 32129 player.off('loadedmetadata', loadedMetadataHandler); 32130 player.off('error', errorHandler); 32131 player.off('dispose', cleanupEvents); 32132 }; 32133 32134 /** 32135 * Cleanup before re-initializing the plugin 32136 * 32137 * @param {Object} [newOptions] an object with plugin options 32138 * @private 32139 */ 32140 var reinitPlugin = function reinitPlugin(newOptions) { 32141 cleanupEvents(); 32142 initPlugin(player, newOptions); 32143 }; 32144 32145 player.on('error', errorHandler); 32146 player.on('dispose', cleanupEvents); 32147 32148 // Overwrite the plugin function so that we can correctly cleanup before 32149 // initializing the plugin 32150 player.reloadSourceOnError = reinitPlugin; 32151}; 32152 32153/** 32154 * Reload the source when an error is detected as long as there 32155 * wasn't an error previously within the last 30 seconds 32156 * 32157 * @param {Object} [options] an object with plugin options 32158 */ 32159var reloadSourceOnError = function reloadSourceOnError(options) { 32160 initPlugin(this, options); 32161}; 32162 32163exports['default'] = reloadSourceOnError; 32164module.exports = exports['default']; 32165}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 32166},{}],14:[function(require,module,exports){ 32167'use strict'; 32168 32169Object.defineProperty(exports, '__esModule', { 32170 value: true 32171}); 32172 32173function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 32174 32175var _playlistJs = require('./playlist.js'); 32176 32177/** 32178 * Enable/disable playlist function. It is intended to have the first two 32179 * arguments partially-applied in order to create the final per-playlist 32180 * function. 32181 * 32182 * @param {PlaylistLoader} playlist - The rendition or media-playlist 32183 * @param {Function} changePlaylistFn - A function to be called after a 32184 * playlist's enabled-state has been changed. Will NOT be called if a 32185 * playlist's enabled-state is unchanged 32186 * @param {Boolean=} enable - Value to set the playlist enabled-state to 32187 * or if undefined returns the current enabled-state for the playlist 32188 * @return {Boolean} The current enabled-state of the playlist 32189 */ 32190var enableFunction = function enableFunction(loader, playlistUri, changePlaylistFn, enable) { 32191 var playlist = loader.master.playlists[playlistUri]; 32192 var blacklisted = (0, _playlistJs.isBlacklisted)(playlist); 32193 var currentlyEnabled = (0, _playlistJs.isEnabled)(playlist); 32194 32195 if (typeof enable === 'undefined') { 32196 return currentlyEnabled; 32197 } 32198 32199 if (enable) { 32200 delete playlist.disabled; 32201 } else { 32202 playlist.disabled = true; 32203 } 32204 32205 if (enable !== currentlyEnabled && !blacklisted) { 32206 // Ensure the outside world knows about our changes 32207 changePlaylistFn(); 32208 if (enable) { 32209 loader.trigger('renditionenabled'); 32210 } else { 32211 loader.trigger('renditiondisabled'); 32212 } 32213 } 32214 return enable; 32215}; 32216 32217/** 32218 * The representation object encapsulates the publicly visible information 32219 * in a media playlist along with a setter/getter-type function (enabled) 32220 * for changing the enabled-state of a particular playlist entry 32221 * 32222 * @class Representation 32223 */ 32224 32225var Representation = function Representation(hlsHandler, playlist, id) { 32226 _classCallCheck(this, Representation); 32227 32228 // Get a reference to a bound version of fastQualityChange_ 32229 var fastChangeFunction = hlsHandler.masterPlaylistController_.fastQualityChange_.bind(hlsHandler.masterPlaylistController_); 32230 32231 // some playlist attributes are optional 32232 if (playlist.attributes.RESOLUTION) { 32233 var resolution = playlist.attributes.RESOLUTION; 32234 32235 this.width = resolution.width; 32236 this.height = resolution.height; 32237 } 32238 32239 this.bandwidth = playlist.attributes.BANDWIDTH; 32240 32241 // The id is simply the ordinality of the media playlist 32242 // within the master playlist 32243 this.id = id; 32244 32245 // Partially-apply the enableFunction to create a playlist- 32246 // specific variant 32247 this.enabled = enableFunction.bind(this, hlsHandler.playlists, playlist.uri, fastChangeFunction); 32248} 32249 32250/** 32251 * A mixin function that adds the `representations` api to an instance 32252 * of the HlsHandler class 32253 * @param {HlsHandler} hlsHandler - An instance of HlsHandler to add the 32254 * representation API into 32255 */ 32256; 32257 32258var renditionSelectionMixin = function renditionSelectionMixin(hlsHandler) { 32259 var playlists = hlsHandler.playlists; 32260 32261 // Add a single API-specific function to the HlsHandler instance 32262 hlsHandler.representations = function () { 32263 return playlists.master.playlists.filter(function (media) { 32264 return !(0, _playlistJs.isBlacklisted)(media); 32265 }).map(function (e, i) { 32266 return new Representation(hlsHandler, e, e.uri); 32267 }); 32268 }; 32269}; 32270 32271exports['default'] = renditionSelectionMixin; 32272module.exports = exports['default']; 32273},{"./playlist.js":11}],15:[function(require,module,exports){ 32274/** 32275 * @file resolve-url.js 32276 */ 32277 32278'use strict'; 32279 32280Object.defineProperty(exports, '__esModule', { 32281 value: true 32282}); 32283 32284function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 32285 32286var _urlToolkit = require('url-toolkit'); 32287 32288var _urlToolkit2 = _interopRequireDefault(_urlToolkit); 32289 32290var _globalWindow = require('global/window'); 32291 32292var _globalWindow2 = _interopRequireDefault(_globalWindow); 32293 32294var resolveUrl = function resolveUrl(baseURL, relativeURL) { 32295 // return early if we don't need to resolve 32296 if (/^[a-z]+:/i.test(relativeURL)) { 32297 return relativeURL; 32298 } 32299 32300 // if the base URL is relative then combine with the current location 32301 if (!/\/\//i.test(baseURL)) { 32302 baseURL = _urlToolkit2['default'].buildAbsoluteURL(_globalWindow2['default'].location.href, baseURL); 32303 } 32304 32305 return _urlToolkit2['default'].buildAbsoluteURL(baseURL, relativeURL); 32306}; 32307 32308exports['default'] = resolveUrl; 32309module.exports = exports['default']; 32310},{"global/window":32,"url-toolkit":63}],16:[function(require,module,exports){ 32311(function (global){ 32312/** 32313 * @file segment-loader.js 32314 */ 32315'use strict'; 32316 32317Object.defineProperty(exports, '__esModule', { 32318 value: true 32319}); 32320 32321var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 32322 32323var _get = function get(_x4, _x5, _x6) { var _again = true; _function: while (_again) { var object = _x4, property = _x5, receiver = _x6; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x4 = parent; _x5 = property; _x6 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 32324 32325function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 32326 32327function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 32328 32329function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
32329onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 32330 32331var _playlist = require('./playlist'); 32332 32333var _playlist2 = _interopRequireDefault(_playlist); 32334 32335var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null); 32336 32337var _videoJs2 = _interopRequireDefault(_videoJs); 32338 32339var _sourceUpdater = require('./source-updater'); 32340 32341var _sourceUpdater2 = _interopRequireDefault(_sourceUpdater); 32342 32343var _config = require('./config'); 32344 32345var _config2 = _interopRequireDefault(_config); 32346 32347var _globalWindow = require('global/window'); 32348 32349var _globalWindow2 = _interopRequireDefault(_globalWindow); 32350 32351var _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs = require('videojs-contrib-media-sources/es5/remove-cues-from-track.js'); 32352 32353var _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs2 = _interopRequireDefault(_videojsContribMediaSourcesEs5RemoveCuesFromTrackJs); 32354 32355var _binUtils = require('./bin-utils'); 32356 32357var _mediaSegmentRequest = require('./media-segment-request'); 32358 32359var _ranges = require('./ranges'); 32360 32361var _playlistSelectors = require('./playlist-selectors'); 32362 32363// in ms 32364var CHECK_BUFFER_DELAY = 500; 32365 32366/** 32367 * Determines if we should call endOfStream on the media source based 32368 * on the state of the buffer or if appened segment was the final 32369 * segment in the playlist. 32370 * 32371 * @param {Object} playlist a media playlist object 32372 * @param {Object} mediaSource the MediaSource object 32373 * @param {Number} segmentIndex the index of segment we last appended 32374 * @returns {Boolean} do we need to call endOfStream on the MediaSource 32375 */ 32376var detectEndOfStream = function detectEndOfStream(playlist, mediaSource, segmentIndex) { 32377 if (!playlist || !mediaSource) { 32378 return false; 32379 } 32380 32381 var segments = playlist.segments; 32382 32383 // determine a few boolean values to help make the branch below easier 32384 // to read 32385 var appendedLastSegment = segmentIndex === segments.length; 32386 32387 // if we've buffered to the end of the video, we need to call endOfStream 32388 // so that MediaSources can trigger the `ended` event when it runs out of 32389 // buffered data instead of waiting for me 32390 return playlist.endList && mediaSource.readyState === 'open' && appendedLastSegment; 32391}; 32392 32393var finite = function finite(num) { 32394 return typeof num === 'number' && isFinite(num); 32395}; 32396 32397var illegalMediaSwitch = function illegalMediaSwitch(loaderType, startingMedia, newSegmentMedia) { 32398 // Although these checks should most likely cover non 'main' types, for now it narrows 32399 // the scope of our checks. 32400 if (loaderType !== 'main' || !startingMedia || !newSegmentMedia) { 32401 return null; 32402 } 32403 32404 if (!newSegmentMedia.containsAudio && !newSegmentMedia.containsVideo) { 32405 return 'Neither audio nor video found in segment.'; 32406 } 32407 32408 if (startingMedia.containsVideo && !newSegmentMedia.containsVideo) { 32409 return 'Only audio found in segment when we expected video.' + ' We can\'t switch to audio only from a stream that had video.' + ' To get rid of this message, please add codec information to the manifest.'; 32410 } 32411 32412 if (!startingMedia.containsVideo && newSegmentMedia.containsVideo) { 32413 return 'Video found in segment when we expected only audio.' + ' We can\'t switch to a stream with video from an audio only stream.' + ' To get rid of this message, please add codec information to the manifest.'; 32414 } 32415 32416 return null; 32417}; 32418 32419exports.illegalMediaSwitch = illegalMediaSwitch; 32420/** 32421 * An object that manages segment loading and appending. 32422 * 32423 * @class SegmentLoader 32424 * @param {Object} options required and optional options 32425 * @extends videojs.EventTarget 32426 */ 32427 32428var SegmentLoader = (function (_videojs$EventTarget) { 32429 _inherits(SegmentLoader, _videojs$EventTarget); 32430 32431 function SegmentLoader(settings) { 32432 var _this = this; 32433 32434 var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; 32435 32436 _classCallCheck(this, SegmentLoader); 32437 32438 _get(Object.getPrototypeOf(SegmentLoader.prototype), 'constructor', this).call(this); 32439 // check pre-conditions 32440 if (!settings) { 32441 throw new TypeError('Initialization settings are required'); 32442 } 32443 if (typeof settings.currentTime !== 'function') { 32444 throw new TypeError('No currentTime getter specified'); 32445 } 32446 if (!settings.mediaSource) { 32447 throw new TypeError('No MediaSource specified'); 32448 } 32449 // public properties 32450 this.state = 'INIT'; 32451 this.bandwidth = settings.bandwidth; 32452 this.throughput = { rate: 0, count: 0 }; 32453 this.roundTrip = NaN; 32454 this.resetStats_(); 32455 this.mediaIndex = null; 32456 32457 // private settings 32458 this.hasPlayed_ = settings.hasPlayed; 32459 this.currentTime_ = settings.currentTime; 32460 this.seekable_ = settings.seekable; 32461 this.seeking_ = settings.seeking; 32462 this.duration_ = settings.duration; 32463 this.mediaSource_ = settings.mediaSource; 32464 this.hls_ = settings.hls; 32465 this.loaderType_ = settings.loaderType; 32466 this.startingMedia_ = void 0; 32467 this.segmentMetadataTrack_ = settings.segmentMetadataTrack; 32468 this.goalBufferLength_ = settings.goalBufferLength; 32469 32470 // private instance variables 32471 this.checkBufferTimeout_ = null; 32472 this.error_ = void 0; 32473 this.currentTimeline_ = -1; 32474 this.pendingSegment_ = null; 32475 this.mimeType_ = null; 32476 this.sourceUpdater_ = null; 32477 this.xhrOptions_ = null; 32478 32479 // Fragmented mp4 playback 32480 this.activeInitSegmentId_ = null; 32481 this.initSegments_ = {}; 32482 32483 this.decrypter_ = settings.decrypter; 32484 32485 // Manages the tracking and generation of sync-points, mappings 32486 // between a time in the display time and a segment index within 32487 // a playlist 32488 this.syncController_ = settings.syncController; 32489 this.syncPoint_ = { 32490 segmentIndex: 0, 32491 time: 0 32492 }; 32493 32494 this.syncController_.on('syncinfoupdate', function () { 32495 return _this.trigger('syncinfoupdate'); 32496 }); 32497 32498 this.mediaSource_.addEventListener('sourceopen', function () { 32499 return _this.ended_ = false;
32500 }); 32501 32502 // ...for determining the fetch location 32503 this.fetchAtBuffer_ = false; 32504 32505 if (options.debug) { 32506 this.logger_ = _videoJs2['default'].log.bind(_videoJs2['default'], 'segment-loader', this.loaderType_, '->'); 32507 } 32508 } 32509 32510 /** 32511 * reset all of our media stats 32512 * 32513 * @private 32514 */ 32515 32516 _createClass(SegmentLoader, [{ 32517 key: 'resetStats_', 32518 value: function resetStats_() { 32519 this.mediaBytesTransferred = 0; 32520 this.mediaRequests = 0; 32521 this.mediaRequestsAborted = 0; 32522 this.mediaRequestsTimedout = 0; 32523 this.mediaRequestsErrored = 0; 32524 this.mediaTransferDuration = 0; 32525 this.mediaSecondsLoaded = 0; 32526 } 32527 32528 /** 32529 * dispose of the SegmentLoader and reset to the default state 32530 */ 32531 }, { 32532 key: 'dispose', 32533 value: function dispose() { 32534 this.state = 'DISPOSED'; 32535 this.pause(); 32536 this.abort_(); 32537 if (this.sourceUpdater_) { 32538 this.sourceUpdater_.dispose(); 32539 } 32540 this.resetStats_(); 32541 } 32542 32543 /** 32544 * abort anything that is currently doing on with the SegmentLoader 32545 * and reset to a default state 32546 */ 32547 }, { 32548 key: 'abort', 32549 value: function abort() { 32550 if (this.state !== 'WAITING') { 32551 if (this.pendingSegment_) { 32552 this.pendingSegment_ = null; 32553 } 32554 return; 32555 } 32556 32557 this.abort_(); 32558 32559 // We aborted the requests we were waiting on, so reset the loader's state to READY 32560 // since we are no longer "waiting" on any requests. XHR callback is not always run 32561 // when the request is aborted. This will prevent the loader from being stuck in the 32562 // WAITING state indefinitely. 32563 this.state = 'READY'; 32564 32565 // don't wait for buffer check timeouts to begin fetching the 32566 // next segment 32567 if (!this.paused()) { 32568 this.monitorBuffer_(); 32569 } 32570 } 32571 32572 /** 32573 * abort all pending xhr requests and null any pending segements 32574 * 32575 * @private 32576 */ 32577 }, { 32578 key: 'abort_', 32579 value: function abort_() { 32580 if (this.pendingSegment_) { 32581 this.pendingSegment_.abortRequests(); 32582 } 32583 32584 // clear out the segment being processed 32585 this.pendingSegment_ = null; 32586 } 32587 32588 /** 32589 * set an error on the segment loader and null out any pending segements 32590 * 32591 * @param {Error} error the error to set on the SegmentLoader 32592 * @return {Error} the error that was set or that is currently set 32593 */ 32594 }, { 32595 key: 'error', 32596 value: function error(_error) { 32597 if (typeof _error !== 'undefined') { 32598 this.error_ = _error; 32599 } 32600 32601 this.pendingSegment_ = null; 32602 return this.error_; 32603 } 32604 }, { 32605 key: 'endOfStream', 32606 value: function endOfStream() { 32607 this.ended_ = true; 32608 this.pause(); 32609 this.trigger('ended'); 32610 } 32611 32612 /** 32613 * Indicates which time ranges are buffered 32614 * 32615 * @return {TimeRange} 32616 * TimeRange object representing the current buffered ranges 32617 */ 32618 }, { 32619 key: 'buffered_', 32620 value: function buffered_() { 32621 if (!this.sourceUpdater_) { 32622 return _videoJs2['default'].createTimeRanges(); 32623 } 32624 32625 return this.sourceUpdater_.buffered(); 32626 } 32627 32628 /** 32629 * Gets and sets init segment for the provided map 32630 * 32631 * @param {Object} map 32632 * The map object representing the init segment to get or set 32633 * @param {Boolean=} set 32634 * If true, the init segment for the provided map should be saved 32635 * @return {Object} 32636 * map object for desired init segment 32637 */ 32638 }, { 32639 key: 'initSegment', 32640 value: function initSegment(map) { 32641 var set = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; 32642 32643 if (!map) { 32644 return null; 32645 } 32646 32647 var id = (0, _binUtils.initSegmentId)(map); 32648 var storedMap = this.initSegments_[id]; 32649 32650 if (set && !storedMap && map.bytes) { 32651 this.initSegments_[id] = storedMap = { 32652 resolvedUri: map.resolvedUri, 32653 byterange: map.byterange, 32654 bytes: map.bytes 32655 }; 32656 } 32657 32658 return storedMap || map; 32659 } 32660 32661 /**
32662 * Returns true if all configuration required for loading is present, otherwise false. 32663 * 32664 * @return {Boolean} True if the all configuration is ready for loading 32665 * @private 32666 */ 32667 }, { 32668 key: 'couldBeginLoading_', 32669 value: function couldBeginLoading_() { 32670 return this.playlist_ && ( 32671 // the source updater is created when init_ is called, so either having a 32672 // source updater or being in the INIT state with a mimeType is enough 32673 // to say we have all the needed configuration to start loading. 32674 this.sourceUpdater_ || this.mimeType_ && this.state === 'INIT') && !this.paused(); 32675 } 32676 32677 /** 32678 * load a playlist and start to fill the buffer 32679 */ 32680 }, { 32681 key: 'load', 32682 value: function load() { 32683 // un-pause 32684 this.monitorBuffer_(); 32685 32686 // if we don't have a playlist yet, keep waiting for one to be 32687 // specified 32688 if (!this.playlist_) { 32689 return; 32690 } 32691 32692 // not sure if this is the best place for this 32693 this.syncController_.setDateTimeMapping(this.playlist_); 32694 32695 // if all the configuration is ready, initialize and begin loading 32696 if (this.state === 'INIT' && this.couldBeginLoading_()) { 32697 return this.init_(); 32698 } 32699 32700 // if we're in the middle of processing a segment already, don't 32701 // kick off an additional segment request 32702 if (!this.couldBeginLoading_() || this.state !== 'READY' && this.state !== 'INIT') { 32703 return; 32704 } 32705 32706 this.state = 'READY'; 32707 } 32708 32709 /** 32710 * Once all the starting parameters have been specified, begin 32711 * operation. This method should only be invoked from the INIT 32712 * state. 32713 * 32714 * @private 32715 */ 32716 }, { 32717 key: 'init_', 32718 value: function init_() { 32719 this.state = 'READY'; 32720 this.sourceUpdater_ = new _sourceUpdater2['default'](this.mediaSource_, this.mimeType_); 32721 this.resetEverything(); 32722 return this.monitorBuffer_(); 32723 } 32724 32725 /** 32726 * set a playlist on the segment loader 32727 * 32728 * @param {PlaylistLoader} media the playlist to set on the segment loader 32729 */ 32730 }, { 32731 key: 'playlist', 32732 value: function playlist(newPlaylist) { 32733 var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; 32734 32735 if (!newPlaylist) { 32736 return; 32737 } 32738 32739 var oldPlaylist = this.playlist_; 32740 var segmentInfo = this.pendingSegment_; 32741 32742 this.playlist_ = newPlaylist; 32743 this.xhrOptions_ = options; 32744 32745 // when we haven't started playing yet, the start of a live playlist 32746 // is always our zero-time so force a sync update each time the playlist 32747 // is refreshed from the server 32748 if (!this.hasPlayed_()) { 32749 newPlaylist.syncInfo = { 32750 mediaSequence: newPlaylist.mediaSequence, 32751 time: 0 32752 }; 32753 } 32754 32755 // in VOD, this is always a rendition switch (or we updated our syncInfo above) 32756 // in LIVE, we always want to update with new playlists (including refreshes) 32757 this.trigger('syncinfoupdate'); 32758 32759 // if we were unpaused but waiting for a playlist, start 32760 // buffering now 32761 if (this.state === 'INIT' && this.couldBeginLoading_()) { 32762 return this.init_(); 32763 } 32764 32765 if (!oldPlaylist || oldPlaylist.uri !== newPlaylist.uri) { 32766 if (this.mediaIndex !== null) { 32767 // we must "resync" the segment loader when we switch renditions and 32768 // the segment loader is already synced to the previous rendition 32769 this.resyncLoader(); 32770 } 32771 32772 // the rest of this function depends on `oldPlaylist` being defined 32773 return; 32774 } 32775 32776 // we reloaded the same playlist so we are in a live scenario 32777 // and we will likely need to adjust the mediaIndex 32778 var mediaSequenceDiff = newPlaylist.mediaSequence - oldPlaylist.mediaSequence; 32779 32780 this.logger_('mediaSequenceDiff', mediaSequenceDiff); 32781 32782 // update the mediaIndex on the SegmentLoader 32783 // this is important because we can abort a request and this value must be 32784 // equal to the last appended mediaIndex 32785 if (this.mediaIndex !== null) { 32786 this.mediaIndex -= mediaSequenceDiff; 32787 } 32788 32789 // update the mediaIndex on the SegmentInfo object 32790 // this is important because we will update this.mediaIndex with this value 32791 // in `handleUpdateEnd_` after the segment has been successfully appended 32792 if (segmentInfo) { 32793 segmentInfo.mediaIndex -= mediaSequenceDiff; 32794 32795 // we need to update the referenced segment so that timing information is 32796 // saved for the new playlist's segment, however, if the segment fell off the 32797 // playlist, we can leave the old reference and just lose the timing info 32798 if (segmentInfo.mediaIndex >= 0) { 32799 segmentInfo.segment = newPlaylist.segments[segmentInfo.mediaIndex]; 32800 } 32801 } 32802 32803 this.syncController_.saveExpiredSegmentInfo(oldPlaylist, newPlaylist); 32804 } 32805 32806 /** 32807 * Prevent the loader from fetching additional segments. If there 32808 * is a segment request outstanding, it will finish processing 32809 * before the loader halts. A segment loader can be unpaused by 32810 * calling load(). 32811 */ 32812 }, { 32813 key: 'pause', 32814 value: function pause() { 32815 if (this.checkBufferTimeout_) { 32816 _globalWindow2['default'].clearTimeout(this.checkBufferTimeout_); 32817 32818 this.checkBufferTimeout_ = null; 32819 } 32820 } 32821 32822 /** 32823 * Returns whether the segment loader is fetching additional 32824 * segments when given the opportunity. This property can be 32825 * modified through calls to pause() and load(). 32826 */ 32827 }, { 32828 key: 'paused', 32829 value: function paused() { 32830 return this.checkBufferTimeout_ === null; 32831 } 32832 32833 /** 32834 * create/set the following mimetype on the SourceBuffer through a 32835 * SourceUpdater 32836 * 32837 * @param {String} mimeType the mime type string to use 32838 */ 32839 }, { 32840 key: 'mimeType', 32841 value: function mimeType(_mimeType) { 32842 if (this.mimeType_) { 32843 return; 32844 } 32845 32846 this.mimeType_ = _mimeType; 32847 // if we were unpaused but waiting for a sourceUpdater, start 32848 // buffering now 32849 if (this.state === 'INIT' && this.couldBeginLoading_()) { 32850 this.init_(); 32851 } 32852 } 32853 32854 /** 32855 * Delete all the buffered data and reset the SegmentLoader 32856 */ 32857 }, { 32858 key: 'resetEverything', 32859 value: function resetEverything() { 32860 this.ended_ = false;
32861 this.resetLoader(); 32862 this.remove(0, this.duration_()); 32863 this.trigger('reseteverything'); 32864 } 32865 32866 /** 32867 * Force the SegmentLoader to resync and start loading around the currentTime instead 32868 * of starting at the end of the buffer 32869 * 32870 * Useful for fast quality changes 32871 */ 32872 }, { 32873 key: 'resetLoader', 32874 value: function resetLoader() { 32875 this.fetchAtBuffer_ = false; 32876 this.resyncLoader(); 32877 } 32878 32879 /** 32880 * Force the SegmentLoader to restart synchronization and make a conservative guess 32881 * before returning to the simple walk-forward method 32882 */ 32883 }, { 32884 key: 'resyncLoader', 32885 value: function resyncLoader() { 32886 this.mediaIndex = null; 32887 this.syncPoint_ = null; 32888 this.abort(); 32889 } 32890 32891 /** 32892 * Remove any data in the source buffer between start and end times 32893 * @param {Number} start - the start time of the region to remove from the buffer 32894 * @param {Number} end - the end time of the region to remove from the buffer 32895 */ 32896 }, { 32897 key: 'remove', 32898 value: function remove(start, end) { 32899 if (this.sourceUpdater_) { 32900 this.sourceUpdater_.remove(start, end); 32901 } 32902 (0, _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs2['default'])(start, end, this.segmentMetadataTrack_); 32903 } 32904 32905 /** 32906 * (re-)schedule monitorBufferTick_ to run as soon as possible 32907 * 32908 * @private 32909 */ 32910 }, { 32911 key: 'monitorBuffer_', 32912 value: function monitorBuffer_() { 32913 if (this.checkBufferTimeout_) { 32914 _globalWindow2['default'].clearTimeout(this.checkBufferTimeout_); 32915 } 32916 32917 this.checkBufferTimeout_ = _globalWindow2['default'].setTimeout(this.monitorBufferTick_.bind(this), 1); 32918 } 32919 32920 /** 32921 * As long as the SegmentLoader is in the READY state, periodically 32922 * invoke fillBuffer_(). 32923 * 32924 * @private 32925 */ 32926 }, { 32927 key: 'monitorBufferTick_', 32928 value: function monitorBufferTick_() { 32929 if (this.state === 'READY') { 32930 this.fillBuffer_(); 32931 } 32932 32933 if (this.checkBufferTimeout_) { 32934 _globalWindow2['default'].clearTimeout(this.checkBufferTimeout_); 32935 } 32936 32937 this.checkBufferTimeout_ = _globalWindow2['default'].setTimeout(this.monitorBufferTick_.bind(this), CHECK_BUFFER_DELAY); 32938 } 32939 32940 /** 32941 * fill the buffer with segements unless the sourceBuffers are 32942 * currently updating 32943 * 32944 * Note: this function should only ever be called by monitorBuffer_ 32945 * and never directly 32946 * 32947 * @private 32948 */ 32949 }, { 32950 key: 'fillBuffer_', 32951 value: function fillBuffer_() { 32952 if (this.sourceUpdater_.updating()) { 32953 return; 32954 } 32955 32956 if (!this.syncPoint_) { 32957 this.syncPoint_ = this.syncController_.getSyncPoint(this.playlist_, this.duration_(), this.currentTimeline_, this.currentTime_()); 32958 } 32959 32960 // see if we need to begin loading immediately 32961 var segmentInfo = this.checkBuffer_(this.buffered_(), this.playlist_, this.mediaIndex, this.hasPlayed_(), this.currentTime_(), this.syncPoint_); 32962 32963 if (!segmentInfo) { 32964 return; 32965 } 32966 32967 var isEndOfStream = detectEndOfStream(this.playlist_, this.mediaSource_, segmentInfo.mediaIndex); 32968 32969 if (isEndOfStream) { 32970 this.endOfStream(); 32971 return; 32972 } 32973 32974 if (segmentInfo.mediaIndex === this.playlist_.segments.length - 1 && this.mediaSource_.readyState === 'ended' && !this.seeking_()) { 32975 return; 32976 } 32977 32978 // We will need to change timestampOffset of the sourceBuffer if either of 32979 // the following conditions are true: 32980 // - The segment.timeline !== this.currentTimeline 32981 // (we are crossing a discontinuity somehow) 32982 // - The "timestampOffset" for the start of this segment is less than 32983 // the currently set timestampOffset 32984 if (segmentInfo.timeline !== this.currentTimeline_ || segmentInfo.startOfSegment !== null && segmentInfo.startOfSegment < this.sourceUpdater_.timestampOffset()) { 32985 this.syncController_.reset(); 32986 segmentInfo.timestampOffset = segmentInfo.startOfSegment; 32987 } 32988 32989 this.loadSegment_(segmentInfo); 32990 } 32991 32992 /** 32993 * Determines what segment request should be made, given current playback 32994 * state. 32995 * 32996 * @param {TimeRanges} buffered - the state of the buffer 32997 * @param {Object} playlist - the playlist object to fetch segments from 32998 * @param {Number} mediaIndex - the previous mediaIndex fetched or null 32999 * @param {Boolean} hasPlayed - a flag indicating whether we have played or not 33000 * @param {Number} currentTime - the playback position in seconds 33001 * @param {Object} syncPoint - a segment info object that describes the 33002 * @returns {Object} a segment request object that describes the segment to load 33003 */ 33004 }, { 33005 key: 'checkBuffer_', 33006 value: function checkBuffer_(buffered, playlist, mediaIndex, hasPlayed, currentTime, syncPoint) { 33007 var lastBufferedEnd = 0; 33008 var startOfSegment = undefined; 33009 33010 if (buffered.length) { 33011 lastBufferedEnd = buffered.end(buffered.length - 1); 33012 } 33013 33014 var bufferedTime = Math.max(0, lastBufferedEnd - currentTime); 33015 33016 if (!playlist.segments.length) { 33017 return null; 33018 } 33019 33020 // if there is plenty of content buffered, and the video has 33021 // been played before relax for awhile 33022 if (bufferedTime >= this.goalBufferLength_()) { 33023 return null; 33024 } 33025 33026 // if the video has not yet played once, and we already have 33027 // one segment downloaded do nothing 33028 if (!hasPlayed && bufferedTime >= 1) { 33029 return null; 33030 } 33031 33032 this.logger_('checkBuffer_', 'mediaIndex:', mediaIndex, 'hasPlayed:', hasPlayed, 'currentTime:', currentTime, 'syncPoint:', syncPoint, 'fetchAtBuffer:', this.fetchAtBuffer_, 'bufferedTime:', bufferedTime); 33033 33034 // When the syncPoint is null, there is no way of determining a good 33035 // conservative segment index to fetch from 33036 // The best thing to do here is to get the kind of sync-point data by 33037 // making a request 33038 if (syncPoint === null) {
vendor: 5,115 bytes, lines 33039-33168
33039 mediaIndex = this.getSyncSegmentCandidate_(playlist); 33040 this.logger_('getSync', 'mediaIndex:', mediaIndex); 33041 return this.generateSegmentInfo_(playlist, mediaIndex, null, true); 33042 } 33043 33044 // Under normal playback conditions fetching is a simple walk forward 33045 if (mediaIndex !== null) { 33046 this.logger_('walkForward', 'mediaIndex:', mediaIndex + 1); 33047 var segment = playlist.segments[mediaIndex]; 33048 33049 if (segment && segment.end) { 33050 startOfSegment = segment.end; 33051 } else { 33052 startOfSegment = lastBufferedEnd; 33053 } 33054 return this.generateSegmentInfo_(playlist, mediaIndex + 1, startOfSegment, false); 33055 } 33056 33057 // There is a sync-point but the lack of a mediaIndex indicates that 33058 // we need to make a good conservative guess about which segment to 33059 // fetch 33060 if (this.fetchAtBuffer_) { 33061 // Find the segment containing the end of the buffer 33062 var mediaSourceInfo = _playlist2['default'].getMediaInfoForTime(playlist, lastBufferedEnd, syncPoint.segmentIndex, syncPoint.time); 33063 33064 mediaIndex = mediaSourceInfo.mediaIndex; 33065 startOfSegment = mediaSourceInfo.startTime; 33066 } else { 33067 // Find the segment containing currentTime 33068 var mediaSourceInfo = _playlist2['default'].getMediaInfoForTime(playlist, currentTime, syncPoint.segmentIndex, syncPoint.time); 33069 33070 mediaIndex = mediaSourceInfo.mediaIndex; 33071 startOfSegment = mediaSourceInfo.startTime; 33072 } 33073 this.logger_('getMediaIndexForTime', 'mediaIndex:', mediaIndex, 'startOfSegment:', startOfSegment); 33074 33075 return this.generateSegmentInfo_(playlist, mediaIndex, startOfSegment, false); 33076 } 33077 33078 /** 33079 * The segment loader has no recourse except to fetch a segment in the 33080 * current playlist and use the internal timestamps in that segment to 33081 * generate a syncPoint. This function returns a good candidate index 33082 * for that process. 33083 * 33084 * @param {Object} playlist - the playlist object to look for a 33085 * @returns {Number} An index of a segment from the playlist to load 33086 */ 33087 }, { 33088 key: 'getSyncSegmentCandidate_', 33089 value: function getSyncSegmentCandidate_(playlist) { 33090 var _this2 = this; 33091 33092 if (this.currentTimeline_ === -1) { 33093 return 0; 33094 } 33095 33096 var segmentIndexArray = playlist.segments.map(function (s, i) { 33097 return { 33098 timeline: s.timeline, 33099 segmentIndex: i 33100 }; 33101 }).filter(function (s) { 33102 return s.timeline === _this2.currentTimeline_; 33103 }); 33104 33105 if (segmentIndexArray.length) { 33106 return segmentIndexArray[Math.min(segmentIndexArray.length - 1, 1)].segmentIndex; 33107 } 33108 33109 return Math.max(playlist.segments.length - 1, 0); 33110 } 33111 }, { 33112 key: 'generateSegmentInfo_', 33113 value: function generateSegmentInfo_(playlist, mediaIndex, startOfSegment, isSyncRequest) { 33114 if (mediaIndex < 0 || mediaIndex >= playlist.segments.length) { 33115 return null; 33116 } 33117 33118 var segment = playlist.segments[mediaIndex]; 33119 33120 return { 33121 requestId: 'segment-loader-' + Math.random(), 33122 // resolve the segment URL relative to the playlist 33123 uri: segment.resolvedUri, 33124 // the segment's mediaIndex at the time it was requested 33125 mediaIndex: mediaIndex, 33126 // whether or not to update the SegmentLoader's state with this 33127 // segment's mediaIndex 33128 isSyncRequest: isSyncRequest, 33129 startOfSegment: startOfSegment, 33130 // the segment's playlist 33131 playlist: playlist, 33132 // unencrypted bytes of the segment 33133 bytes: null, 33134 // when a key is defined for this segment, the encrypted bytes 33135 encryptedBytes: null, 33136 // The target timestampOffset for this segment when we append it 33137 // to the source buffer 33138 timestampOffset: null, 33139 // The timeline that the segment is in 33140 timeline: segment.timeline, 33141 // The expected duration of the segment in seconds 33142 duration: segment.duration, 33143 // retain the segment in case the playlist updates while doing an async process 33144 segment: segment 33145 }; 33146 } 33147 33148 /** 33149 * Determines if the network has enough bandwidth to complete the current segment 33150 * request in a timely manner. If not, the request will be aborted early and bandwidth 33151 * updated to trigger a playlist switch. 33152 * 33153 * @param {Object} stats 33154 * Object containing stats about the request timing and size 33155 * @return {Boolean} True if the request was aborted, false otherwise 33156 * @private 33157 */ 33158 }, { 33159 key: 'abortRequestEarly_', 33160 value: function abortRequestEarly_(stats) { 33161 if (this.hls_.tech_.paused() || 33162 // Don't abort if the current playlist is on the lowestEnabledRendition 33163 // TODO: Replace using timeout with a boolean indicating whether this playlist is 33164 // the lowestEnabledRendition. 33165 !this.xhrOptions_.timeout || 33166 // Don't abort if we have no bandwidth information to estimate segment sizes 33167 !this.playlist_.attributes.BANDWIDTH) { 33168 return false;
33169 } 33170 33171 // Wait at least 1 second since the first byte of data has been received before 33172 // using the calculated bandwidth from the progress event to allow the bitrate 33173 // to stabilize 33174 if (Date.now() - (stats.firstBytesReceivedAt || Date.now()) < 1000) { 33175 return false; 33176 } 33177 33178 var currentTime = this.currentTime_(); 33179 var measuredBandwidth = stats.bandwidth; 33180 var segmentDuration = this.pendingSegment_.duration; 33181 33182 var requestTimeRemaining = _playlist2['default'].estimateSegmentRequestTime(segmentDuration, measuredBandwidth, this.playlist_, stats.bytesReceived); 33183 33184 // Subtract 1 from the timeUntilRebuffer so we still consider an early abort 33185 // if we are only left with less than 1 second when the request completes. 33186 // A negative timeUntilRebuffering indicates we are already rebuffering 33187 var timeUntilRebuffer = (0, _ranges.timeUntilRebuffer)(this.buffered_(), currentTime, this.hls_.tech_.playbackRate()) - 1; 33188 33189 // Only consider aborting early if the estimated time to finish the download 33190 // is larger than the estimated time until the player runs out of forward buffer 33191 if (requestTimeRemaining <= timeUntilRebuffer) { 33192 return false; 33193 } 33194 33195 var switchCandidate = (0, _playlistSelectors.minRebufferMaxBandwidthSelector)({ 33196 master: this.hls_.playlists.master, 33197 currentTime: currentTime, 33198 bandwidth: measuredBandwidth, 33199 duration: this.duration_(), 33200 segmentDuration: segmentDuration, 33201 timeUntilRebuffer: timeUntilRebuffer, 33202 currentTimeline: this.currentTimeline_, 33203 syncController: this.syncController_ 33204 }); 33205 33206 if (!switchCandidate) { 33207 return; 33208 } 33209 33210 var rebufferingImpact = requestTimeRemaining - timeUntilRebuffer; 33211 33212 var timeSavedBySwitching = rebufferingImpact - switchCandidate.rebufferingImpact; 33213 33214 var minimumTimeSaving = 0.5; 33215 33216 // If we are already rebuffering, increase the amount of variance we add to the 33217 // potential round trip time of the new request so that we are not too aggressive 33218 // with switching to a playlist that might save us a fraction of a second. 33219 if (timeUntilRebuffer <= _ranges.TIME_FUDGE_FACTOR) { 33220 minimumTimeSaving = 1; 33221 } 33222 33223 if (!switchCandidate.playlist || switchCandidate.playlist.uri === this.playlist_.uri || timeSavedBySwitching < minimumTimeSaving) { 33224 return false; 33225 } 33226 33227 // set the bandwidth to that of the desired playlist being sure to scale by 33228 // BANDWIDTH_VARIANCE and add one so the playlist selector does not exclude it 33229 // don't trigger a bandwidthupdate as the bandwidth is artifial 33230 this.bandwidth = switchCandidate.playlist.attributes.BANDWIDTH * _config2['default'].BANDWIDTH_VARIANCE + 1; 33231 this.abort(); 33232 this.trigger('earlyabort'); 33233 return true; 33234 } 33235 33236 /** 33237 * XHR `progress` event handler 33238 * 33239 * @param {Event} 33240 * The XHR `progress` event 33241 * @param {Object} simpleSegment 33242 * A simplified segment object copy 33243 * @private 33244 */ 33245 }, { 33246 key: 'handleProgress_', 33247 value: function handleProgress_(event, simpleSegment) { 33248 if (!this.pendingSegment_ || simpleSegment.requestId !== this.pendingSegment_.requestId || this.abortRequestEarly_(simpleSegment.stats)) { 33249 return; 33250 } 33251 33252 this.trigger('progress'); 33253 } 33254 33255 /** 33256 * load a specific segment from a request into the buffer 33257 * 33258 * @private 33259 */ 33260 }, { 33261 key: 'loadSegment_', 33262 value: function loadSegment_(segmentInfo) { 33263 this.state = 'WAITING'; 33264 this.pendingSegment_ = segmentInfo; 33265 this.trimBackBuffer_(segmentInfo); 33266 33267 segmentInfo.abortRequests = (0, _mediaSegmentRequest.mediaSegmentRequest)(this.hls_.xhr, this.xhrOptions_, this.decrypter_, this.createSimplifiedSegmentObj_(segmentInfo), 33268 // progress callback 33269 this.handleProgress_.bind(this), this.segmentRequestFinished_.bind(this)); 33270 } 33271 33272 /** 33273 * trim the back buffer so that we don't have too much data 33274 * in the source buffer 33275 * 33276 * @private 33277 * 33278 * @param {Object} segmentInfo - the current segment 33279 */ 33280 }, { 33281 key: 'trimBackBuffer_', 33282 value: function trimBackBuffer_(segmentInfo) { 33283 var seekable = this.seekable_(); 33284 var currentTime = this.currentTime_(); 33285 var removeToTime = 0; 33286 33287 // Chrome has a hard limit of 150MB of 33288 // buffer and a very conservative "garbage collector" 33289 // We manually clear out the old buffer to ensure 33290 // we don't trigger the QuotaExceeded error 33291 // on the source buffer during subsequent appends 33292 33293 // If we have a seekable range use that as the limit for what can be removed safely 33294 // otherwise remove anything older than 30 seconds before the current play head 33295 if (seekable.length && seekable.start(0) > 0 && seekable.start(0) < currentTime) { 33296 removeToTime = seekable.start(0); 33297 } else { 33298 removeToTime = currentTime - 30; 33299 } 33300 33301 if (removeToTime > 0) { 33302 this.remove(0, removeToTime); 33303 } 33304 } 33305 33306 /** 33307 * created a simplified copy of the segment object with just the 33308 * information necessary to perform the XHR and decryption 33309 * 33310 * @private 33311 * 33312 * @param {Object} segmentInfo - the current segment 33313 * @returns {Object}
vendor: 9,589 bytes, lines 33313-33586
33313 a simplified segment object copy 33314 */ 33315 }, { 33316 key: 'createSimplifiedSegmentObj_', 33317 value: function createSimplifiedSegmentObj_(segmentInfo) { 33318 var segment = segmentInfo.segment; 33319 var simpleSegment = { 33320 resolvedUri: segment.resolvedUri, 33321 byterange: segment.byterange, 33322 requestId: segmentInfo.requestId 33323 }; 33324 33325 if (segment.key) { 33326 // if the media sequence is greater than 2^32, the IV will be incorrect 33327 // assuming 10s segments, that would be about 1300 years 33328 var iv = segment.key.iv || new Uint32Array([0, 0, 0, segmentInfo.mediaIndex + segmentInfo.playlist.mediaSequence]); 33329 33330 simpleSegment.key = { 33331 resolvedUri: segment.key.resolvedUri, 33332 iv: iv 33333 }; 33334 } 33335 33336 if (segment.map) { 33337 simpleSegment.map = this.initSegment(segment.map); 33338 } 33339 33340 return simpleSegment; 33341 } 33342 33343 /** 33344 * Handle the callback from the segmentRequest function and set the 33345 * associated SegmentLoader state and errors if necessary 33346 * 33347 * @private 33348 */ 33349 }, { 33350 key: 'segmentRequestFinished_', 33351 value: function segmentRequestFinished_(error, simpleSegment) { 33352 // every request counts as a media request even if it has been aborted 33353 // or canceled due to a timeout 33354 this.mediaRequests += 1; 33355 33356 if (simpleSegment.stats) { 33357 this.mediaBytesTransferred += simpleSegment.stats.bytesReceived; 33358 this.mediaTransferDuration += simpleSegment.stats.roundTripTime; 33359 } 33360 33361 // The request was aborted and the SegmentLoader has already been reset 33362 if (!this.pendingSegment_) { 33363 this.mediaRequestsAborted += 1; 33364 return; 33365 } 33366 33367 // the request was aborted and the SegmentLoader has already started 33368 // another request. this can happen when the timeout for an aborted 33369 // request triggers due to a limitation in the XHR library 33370 // do not count this as any sort of request or we risk double-counting 33371 if (simpleSegment.requestId !== this.pendingSegment_.requestId) { 33372 return; 33373 } 33374 33375 // an error occurred from the active pendingSegment_ so reset everything 33376 if (error) { 33377 this.pendingSegment_ = null; 33378 this.state = 'READY'; 33379 33380 // the requests were aborted just record the aborted stat and exit 33381 // this is not a true error condition and nothing corrective needs 33382 // to be done 33383 if (error.code === _mediaSegmentRequest.REQUEST_ERRORS.ABORTED) { 33384 this.mediaRequestsAborted += 1; 33385 return; 33386 } 33387 33388 this.pause(); 33389 33390 // the error is really just that at least one of the requests timed-out 33391 // set the bandwidth to a very low value and trigger an ABR switch to 33392 // take emergency action 33393 if (error.code === _mediaSegmentRequest.REQUEST_ERRORS.TIMEOUT) { 33394 this.mediaRequestsTimedout += 1; 33395 this.bandwidth = 1; 33396 this.roundTrip = NaN; 33397 this.trigger('bandwidthupdate'); 33398 return; 33399 } 33400 33401 // if control-flow has arrived here, then the error is real 33402 // emit an error event to blacklist the current playlist 33403 this.mediaRequestsErrored += 1; 33404 this.error(error); 33405 this.trigger('error'); 33406 return; 33407 } 33408 33409 // the response was a success so set any bandwidth stats the request 33410 // generated for ABR purposes 33411 this.bandwidth = simpleSegment.stats.bandwidth; 33412 this.roundTrip = simpleSegment.stats.roundTripTime; 33413 33414 // if this request included an initialization segment, save that data 33415 // to the initSegment cache 33416 if (simpleSegment.map) { 33417 simpleSegment.map = this.initSegment(simpleSegment.map, true); 33418 } 33419 33420 this.processSegmentResponse_(simpleSegment); 33421 } 33422 33423 /** 33424 * Move any important data from the simplified segment object 33425 * back to the real segment object for future phases 33426 * 33427 * @private 33428 */ 33429 }, { 33430 key: 'processSegmentResponse_', 33431 value: function processSegmentResponse_(simpleSegment) { 33432 var segmentInfo = this.pendingSegment_; 33433 33434 segmentInfo.bytes = simpleSegment.bytes; 33435 if (simpleSegment.map) { 33436 segmentInfo.segment.map.bytes = simpleSegment.map.bytes; 33437 } 33438 33439 segmentInfo.endOfAllRequests = simpleSegment.endOfAllRequests; 33440 this.handleSegment_(); 33441 } 33442 33443 /** 33444 * append a decrypted segement to the SourceBuffer through a SourceUpdater 33445 * 33446 * @private 33447 */ 33448 }, { 33449 key: 'handleSegment_', 33450 value: function handleSegment_() { 33451 var _this3 = this; 33452 33453 if (!this.pendingSegment_) { 33454 this.state = 'READY'; 33455 return; 33456 } 33457 33458 var segmentInfo = this.pendingSegment_; 33459 var segment = segmentInfo.segment; 33460 var timingInfo = this.syncController_.probeSegmentInfo(segmentInfo); 33461 33462 // When we have our first timing info, determine what media types this loader is 33463 // dealing with. Although we're maintaining extra state, it helps to preserve the 33464 // separation of segment loader from the actual source buffers. 33465 if (typeof this.startingMedia_ === 'undefined' && timingInfo && ( 33466 // Guard against cases where we're not getting timing info at all until we are 33467 // certain that all streams will provide it. 33468 timingInfo.containsAudio || timingInfo.containsVideo)) { 33469 this.startingMedia_ = { 33470 containsAudio: timingInfo.containsAudio, 33471 containsVideo: timingInfo.containsVideo 33472 }; 33473 } 33474 33475 var illegalMediaSwitchError = illegalMediaSwitch(this.loaderType_, this.startingMedia_, timingInfo); 33476 33477 if (illegalMediaSwitchError) { 33478 this.error({ 33479 message: illegalMediaSwitchError 33480 }); 33481 this.trigger('error'); 33482 return; 33483 } 33484 33485 this.state = 'APPENDING'; 33486 33487 if (segmentInfo.isSyncRequest) { 33488 this.trigger('syncinfoupdate'); 33489 this.pendingSegment_ = null; 33490 this.state = 'READY'; 33491 return; 33492 } 33493 33494 if (segmentInfo.timestampOffset !== null && segmentInfo.timestampOffset !== this.sourceUpdater_.timestampOffset()) { 33495 this.sourceUpdater_.timestampOffset(segmentInfo.timestampOffset); 33496 // fired when a timestamp offset is set in HLS (can also identify discontinuities) 33497 this.trigger('timestampoffset'); 33498 } 33499 33500 // if the media initialization segment is changing, append it 33501 // before the content segment 33502 if (segment.map) { 33503 (function () { 33504 var initId = (0, _binUtils.initSegmentId)(segment.map); 33505 33506 if (!_this3.activeInitSegmentId_ || _this3.activeInitSegmentId_ !== initId) { 33507 var initSegment = _this3.initSegment(segment.map); 33508 33509 _this3.sourceUpdater_.appendBuffer(initSegment.bytes, function () { 33510 _this3.activeInitSegmentId_ = initId; 33511 }); 33512 } 33513 })(); 33514 } 33515 33516 segmentInfo.byteLength = segmentInfo.bytes.byteLength; 33517 if (typeof segment.start === 'number' && typeof segment.end === 'number') { 33518 this.mediaSecondsLoaded += segment.end - segment.start; 33519 } else { 33520 this.mediaSecondsLoaded += segment.duration; 33521 } 33522 33523 this.sourceUpdater_.appendBuffer(segmentInfo.bytes, this.handleUpdateEnd_.bind(this)); 33524 } 33525 33526 /** 33527 * callback to run when appendBuffer is finished. detects if we are 33528 * in a good state to do things with the data we got, or if we need 33529 * to wait for more 33530 * 33531 * @private 33532 */ 33533 }, { 33534 key: 'handleUpdateEnd_', 33535 value: function handleUpdateEnd_() { 33536 this.logger_('handleUpdateEnd_', 'segmentInfo:', this.pendingSegment_); 33537 33538 if (!this.pendingSegment_) { 33539 this.state = 'READY'; 33540 if (!this.paused()) { 33541 this.monitorBuffer_(); 33542 } 33543 return; 33544 } 33545 33546 var segmentInfo = this.pendingSegment_; 33547 var segment = segmentInfo.segment; 33548 var isWalkingForward = this.mediaIndex !== null; 33549 33550 this.pendingSegment_ = null; 33551 this.recordThroughput_(segmentInfo); 33552 this.addSegmentMetadataCue_(segmentInfo); 33553 33554 this.state = 'READY'; 33555 33556 this.mediaIndex = segmentInfo.mediaIndex; 33557 this.fetchAtBuffer_ = true; 33558 this.currentTimeline_ = segmentInfo.timeline; 33559 33560 // We must update the syncinfo to recalculate the seekable range before 33561 // the following conditional otherwise it may consider this a bad "guess" 33562 // and attempt to resync when the post-update seekable window and live 33563 // point would mean that this was the perfect segment to fetch 33564 this.trigger('syncinfoupdate'); 33565 33566 // If we previously appended a segment that ends more than 3 targetDurations before 33567 // the currentTime_ that means that our conservative guess was too conservative. 33568 // In that case, reset the loader state so that we try to use any information gained 33569 // from the previous request to create a new, more accurate, sync-point. 33570 if (segment.end && this.currentTime_() - segment.end > segmentInfo.playlist.targetDuration * 3) { 33571 this.resetEverything(); 33572 return; 33573 } 33574 33575 // Don't do a rendition switch unless we have enough time to get a sync segment 33576 // and conservatively guess 33577 if (isWalkingForward) { 33578 this.trigger('bandwidthupdate'); 33579 } 33580 this.trigger('progress'); 33581 33582 // any time an update finishes and the last segment is in the 33583 // buffer, end the stream. this ensures the "ended" event will 33584 // fire if playback reaches that point. 33585 var isEndOfStream = detectEndOfStream(segmentInfo.playlist, this.mediaSource_, segmentInfo.mediaIndex + 1); 33586
33587 if (isEndOfStream) { 33588 this.endOfStream(); 33589 } 33590 33591 if (!this.paused()) { 33592 this.monitorBuffer_(); 33593 } 33594 } 33595 33596 /** 33597 * Records the current throughput of the decrypt, transmux, and append 33598 * portion of the semgment pipeline. `throughput.rate` is a the cumulative 33599 * moving average of the throughput. `throughput.count` is the number of 33600 * data points in the average. 33601 * 33602 * @private 33603 * @param {Object} segmentInfo the object returned by loadSegment 33604 */ 33605 }, { 33606 key: 'recordThroughput_', 33607 value: function recordThroughput_(segmentInfo) { 33608 var rate = this.throughput.rate; 33609 // Add one to the time to ensure that we don't accidentally attempt to divide 33610 // by zero in the case where the throughput is ridiculously high 33611 var segmentProcessingTime = Date.now() - segmentInfo.endOfAllRequests + 1; 33612 // Multiply by 8000 to convert from bytes/millisecond to bits/second 33613 var segmentProcessingThroughput = Math.floor(segmentInfo.byteLength / segmentProcessingTime * 8 * 1000); 33614 33615 // This is just a cumulative moving average calculation: 33616 // newAvg = oldAvg + (sample - oldAvg) / (sampleCount + 1) 33617 this.throughput.rate += (segmentProcessingThroughput - rate) / ++this.throughput.count; 33618 } 33619 33620 /** 33621 * A debugging logger noop that is set to console.log only if debugging 33622 * is enabled globally 33623 * 33624 * @private 33625 */ 33626 }, { 33627 key: 'logger_', 33628 value: function logger_() {} 33629 33630 /** 33631 * Adds a cue to the segment-metadata track with some metadata information about the 33632 * segment 33633 * 33634 * @private 33635 * @param {Object} segmentInfo 33636 * the object returned by loadSegment 33637 * @method addSegmentMetadataCue_ 33638 */ 33639 }, { 33640 key: 'addSegmentMetadataCue_', 33641 value: function addSegmentMetadataCue_(segmentInfo) { 33642 if (!this.segmentMetadataTrack_) { 33643 return; 33644 } 33645 33646 var segment = segmentInfo.segment; 33647 var start = segment.start; 33648 var end = segment.end; 33649 33650 // Do not try adding the cue if the start and end times are invalid. 33651 if (!finite(start) || !finite(end)) { 33652 return; 33653 } 33654 33655 (0, _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs2['default'])(start, end, this.segmentMetadataTrack_); 33656 33657 var Cue = _globalWindow2['default'].WebKitDataCue || _globalWindow2['default'].VTTCue; 33658 var value = { 33659 uri: segmentInfo.uri, 33660 timeline: segmentInfo.timeline, 33661 playlist: segmentInfo.playlist.uri, 33662 start: start, 33663 end: end 33664 }; 33665 var data = JSON.stringify(value); 33666 var cue = new Cue(start, end, data); 33667 33668 // Attach the metadata to the value property of the cue to keep consistency between 33669 // the differences of WebKitDataCue in safari and VTTCue in other browsers 33670 cue.value = value; 33671 33672 this.segmentMetadataTrack_.addCue(cue); 33673 } 33674 }]); 33675 33676 return SegmentLoader; 33677})(_videoJs2['default'].EventTarget); 33678 33679exports['default'] = SegmentLoader; 33680}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 33681},{"./bin-utils":2,"./config":3,"./media-segment-request":7,"./playlist":11,"./playlist-selectors":10,"./ranges":12,"./source-updater":17,"global/window":32,"videojs-contrib-media-sources/es5/remove-cues-from-track.js":72}],17:[function(require,module,exports){ 33682(function (global){ 33683/** 33684 * @file source-updater.js 33685 */ 33686'use strict'; 33687 33688Object.defineProperty(exports, '__esModule', { 33689 value: true 33690}); 33691 33692var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 33693 33694function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 33695 33696function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 33697 33698var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null); 33699 33700var _videoJs2 = _interopRequireDefault(_videoJs); 33701 33702var noop = function noop() {}; 33703 33704/** 33705 * A queue of callbacks to be serialized and applied when a
33706 * MediaSource and its associated SourceBuffers are not in the 33707 * updating state. It is used by the segment loader to update the 33708 * underlying SourceBuffers when new data is loaded, for instance. 33709 * 33710 * @class SourceUpdater 33711 * @param {MediaSource} mediaSource the MediaSource to create the 33712 * SourceBuffer from 33713 * @param {String} mimeType the desired MIME type of the underlying 33714 * SourceBuffer 33715 */ 33716 33717var SourceUpdater = (function () { 33718 function SourceUpdater(mediaSource, mimeType) { 33719 var _this = this; 33720 33721 _classCallCheck(this, SourceUpdater); 33722 33723 var createSourceBuffer = function createSourceBuffer() { 33724 _this.sourceBuffer_ = mediaSource.addSourceBuffer(mimeType); 33725 33726 // run completion handlers and process callbacks as updateend 33727 // events fire 33728 _this.onUpdateendCallback_ = function () { 33729 var pendingCallback = _this.pendingCallback_; 33730 33731 _this.pendingCallback_ = null; 33732 33733 if (pendingCallback) { 33734 pendingCallback(); 33735 } 33736 33737 _this.runCallback_(); 33738 }; 33739 33740 _this.sourceBuffer_.addEventListener('updateend', _this.onUpdateendCallback_); 33741 33742 _this.runCallback_(); 33743 }; 33744 33745 this.callbacks_ = []; 33746 this.pendingCallback_ = null; 33747 this.timestampOffset_ = 0; 33748 this.mediaSource = mediaSource; 33749 this.processedAppend_ = false; 33750 33751 if (mediaSource.readyState === 'closed') { 33752 mediaSource.addEventListener('sourceopen', createSourceBuffer); 33753 } else { 33754 createSourceBuffer(); 33755 } 33756 } 33757 33758 /** 33759 * Aborts the current segment and resets the segment parser. 33760 * 33761 * @param {Function} done function to call when done 33762 * @see http://w3c.github.io/media-source/#widl-SourceBuffer-abort-void 33763 */ 33764 33765 _createClass(SourceUpdater, [{ 33766 key: 'abort', 33767 value: function abort(done) { 33768 var _this2 = this; 33769 33770 if (this.processedAppend_) { 33771 this.queueCallback_(function () { 33772 _this2.sourceBuffer_.abort(); 33773 }, done); 33774 } 33775 } 33776 33777 /** 33778 * Queue an update to append an ArrayBuffer. 33779 * 33780 * @param {ArrayBuffer} bytes 33781 * @param {Function} done the function to call when done 33782 * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-appendBuffer-void-ArrayBuffer-data 33783 */ 33784 }, { 33785 key: 'appendBuffer', 33786 value: function appendBuffer(bytes, done) { 33787 var _this3 = this; 33788 33789 this.processedAppend_ = true; 33790 33791 this.queueCallback_(function () { 33792 _this3.sourceBuffer_.appendBuffer(bytes); 33793 }, done); 33794 } 33795 33796 /** 33797 * Indicates what TimeRanges are buffered in the managed SourceBuffer. 33798 * 33799 * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-buffered 33800 */ 33801 }, { 33802 key: 'buffered', 33803 value: function buffered() { 33804 if (!this.sourceBuffer_) { 33805 return _videoJs2['default'].createTimeRanges(); 33806 } 33807 return this.sourceBuffer_.buffered; 33808 } 33809 33810 /** 33811 * Queue an update to remove a time range from the buffer. 33812 * 33813 * @param {Number} start where to start the removal 33814 * @param {Number} end where to end the removal 33815 * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-remove-void-double-start-unrestricted-double-end 33816 */ 33817 }, { 33818 key: 'remove', 33819 value: function remove(start, end) { 33820 var _this4 = this; 33821 33822 if (this.processedAppend_) { 33823 this.queueCallback_(function () { 33824 _this4.sourceBuffer_.remove(start, end); 33825 }, noop); 33826 } 33827 } 33828 33829 /** 33830 * Whether the underlying sourceBuffer is updating or not 33831 * 33832 * @return {Boolean} the updating status of the SourceBuffer 33833 */ 33834 }, { 33835 key: 'updating', 33836 value: function updating() { 33837 return !this.sourceBuffer_ || this.sourceBuffer_.updating || this.pendingCallback_; 33838 } 33839 33840 /** 33841 * Set/get the timestampoffset on the SourceBuffer 33842 * 33843 * @return {Number} the timestamp offset 33844 */ 33845 }, { 33846 key: 'timestampOffset', 33847 value: function timestampOffset(offset) { 33848 var _this5 = this; 33849 33850 if (typeof offset !== 'undefined') { 33851 this.queueCallback_(function () { 33852 _this5.sourceBuffer_.timestampOffset = offset; 33853 }); 33854 this.timestampOffset_ = offset; 33855 } 33856 return this.timestampOffset_; 33857 } 33858 33859 /** 33860 * Queue a callback to run 33861 */ 33862 }, { 33863 key: 'queueCallback_', 33864 value: function queueCallback_(callback, done) { 33865 this.callbacks_.push([callback.bind(this), done]); 33866 this.runCallback_(); 33867 } 33868 33869 /** 33870 * Run a queued callback 33871 */ 33872 }, { 33873 key: 'runCallback_', 33874 value: function runCallback_() { 33875 var callbacks = undefined; 33876 33877 if (!this.updating() && this.callbacks_.length) { 33878 callbacks = this.callbacks_.shift(); 33879 this.pendingCallback_ = callbacks[1]; 33880 callbacks[0](); 33881 } 33882 } 33883 33884 /** 33885 * dispose of the source updater and the underlying sourceBuffer 33886 */ 33887 }, { 33888 key: 'dispose', 33889 value: function dispose() { 33890 this.sourceBuffer_.removeEventListener('updateend', this.onUpdateendCallback_); 33891 if (this.sourceBuffer_ && this.mediaSource.readyState === 'open') { 33892 this.sourceBuffer_.abort(); 33893 } 33894 } 33895 }]); 33896 33897 return SourceUpdater; 33898})(); 33899 33900exports['default'] = SourceUpdater; 33901module.exports = exports['default']; 33902}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 33903},{}],18:[function(require,module,exports){ 33904(function (global){ 33905/** 33906 * @file sync-controller.js 33907 */ 33908 33909'use strict'; 33910 33911Object.defineProperty(exports, '__esModule', { 33912 value: true 33913}); 33914 33915var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 33916 33917var _get = function get(_x2, _x3, _x4) { var _again = true; _function: while (_again) { var object = _x2, property = _x3, receiver = _x4; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x2 = parent; _x3 = property; _x4 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 33918 33919function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 33920 33921function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 33922 33923function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
33923onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 33924 33925var _muxJsLibMp4Probe = require('mux.js/lib/mp4/probe'); 33926 33927var _muxJsLibMp4Probe2 = _interopRequireDefault(_muxJsLibMp4Probe); 33928 33929var _muxJsLibToolsTsInspectorJs = require('mux.js/lib/tools/ts-inspector.js'); 33930 33931var _playlist = require('./playlist'); 33932 33933var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null); 33934 33935var _videoJs2 = _interopRequireDefault(_videoJs); 33936 33937var syncPointStrategies = [ 33938// Stategy "VOD": Handle the VOD-case where the sync-point is *always* 33939// the equivalence display-time 0 === segment-index 0 33940{ 33941 name: 'VOD', 33942 run: function run(syncController, playlist, duration, currentTimeline, currentTime) { 33943 if (duration !== Infinity) { 33944 var syncPoint = { 33945 time: 0, 33946 segmentIndex: 0 33947 }; 33948 33949 return syncPoint; 33950 } 33951 return null; 33952 } 33953}, 33954// Stategy "ProgramDateTime": We have a program-date-time tag in this playlist 33955{ 33956 name: 'ProgramDateTime', 33957 run: function run(syncController, playlist, duration, currentTimeline, currentTime) { 33958 if (syncController.datetimeToDisplayTime && playlist.dateTimeObject) { 33959 var playlistTime = playlist.dateTimeObject.getTime() / 1000; 33960 var playlistStart = playlistTime + syncController.datetimeToDisplayTime; 33961 var syncPoint = { 33962 time: playlistStart, 33963 segmentIndex: 0 33964 }; 33965 33966 return syncPoint; 33967 } 33968 return null; 33969 } 33970}, 33971// Stategy "Segment": We have a known time mapping for a timeline and a 33972// segment in the current timeline with timing data 33973{ 33974 name: 'Segment', 33975 run: function run(syncController, playlist, duration, currentTimeline, currentTime) { 33976 var segments = playlist.segments || []; 33977 var syncPoint = null; 33978 var lastDistance = null; 33979 33980 currentTime = currentTime || 0; 33981 33982 for (var i = 0; i < segments.length; i++) { 33983 var segment = segments[i]; 33984 33985 if (segment.timeline === currentTimeline && typeof segment.start !== 'undefined') { 33986 var distance = Math.abs(currentTime - segment.start); 33987 33988 // Once the distance begins to increase, we have passed 33989 // currentTime and can stop looking for better candidates 33990 if (lastDistance !== null && lastDistance < distance) { 33991 break; 33992 } 33993 33994 if (!syncPoint || lastDistance === null || lastDistance >= distance) { 33995 lastDistance = distance; 33996 syncPoint = { 33997 time: segment.start, 33998 segmentIndex: i 33999 }; 34000 } 34001 } 34002 } 34003 return syncPoint; 34004 } 34005}, 34006// Stategy "Discontinuity": We have a discontinuity with a known 34007// display-time 34008{ 34009 name: 'Discontinuity', 34010 run: function run(syncController, playlist, duration, currentTimeline, currentTime) { 34011 var syncPoint = null; 34012 34013 currentTime = currentTime || 0; 34014 34015 if (playlist.discontinuityStarts && playlist.discontinuityStarts.length) { 34016 var lastDistance = null; 34017 34018 for (var i = 0; i < playlist.discontinuityStarts.length; i++) { 34019 var segmentIndex = playlist.discontinuityStarts[i]; 34020 var discontinuity = playlist.discontinuitySequence + i + 1; 34021 var discontinuitySync = syncController.discontinuities[discontinuity]; 34022 34023 if (discontinuitySync) { 34024 var distance = Math.abs(currentTime - discontinuitySync.time); 34025 34026 // Once the distance begins to increase, we have passed 34027 // currentTime and can stop looking for better candidates 34028 if (lastDistance !== null && lastDistance < distance) { 34029 break; 34030 } 34031 34032 if (!syncPoint || lastDistance === null || lastDistance >= distance) { 34033 lastDistance = distance; 34034 syncPoint = { 34035 time: discontinuitySync.time, 34036 segmentIndex: segmentIndex 34037 }; 34038 } 34039 } 34040 } 34041 } 34042 return syncPoint; 34043 } 34044}, 34045// Stategy "Playlist": We have a playlist with a known mapping of 34046// segment index to display time 34047{ 34048 name: 'Playlist', 34049 run: function run(syncController, playlist, duration, currentTimeline, currentTime) { 34050 if (playlist.syncInfo) { 34051 var syncPoint = { 34052 time: playlist.syncInfo.time, 34053 segmentIndex: playlist.syncInfo.mediaSequence - playlist.mediaSequence 34054 }; 34055 34056 return syncPoint; 34057 } 34058 return null; 34059 } 34060}]; 34061 34062exports.syncPointStrategies = syncPointStrategies; 34063 34064var SyncController = (function (_videojs$EventTarget) { 34065 _inherits(SyncController, _videojs$EventTarget); 34066 34067 function SyncController() { 34068 var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; 34069 34070 _classCallCheck(this, SyncController); 34071 34072 _get(Object.getPrototypeOf(SyncController.prototype), 'constructor', this).call(this); 34073 // Segment Loader state variables... 34074 // ...for synching across variants 34075 this.inspectCache_ = undefined; 34076 34077 // ...for synching across variants 34078 this.timelines = []; 34079 this.discontinuities = []; 34080 this.datetimeToDisplayTime = null; 34081 34082 if (options.debug) { 34083 this.logger_ = _videoJs2['default'].log.bind(_videoJs2['default'], 'sync-controller ->'); 34084 } 34085 } 34086 34087 /** 34088 * Find a sync-point for the playlist specified 34089 * 34090 * A sync-point is defined as a known mapping from display-time to 34091 * a segment-index in the current playlist. 34092 * 34093 * @param {Playlist} playlist 34094 * The playlist that needs a sync-point 34095 * @param {Number} duration 34096 * Duration of the MediaSource (Infinite if playing a live source) 34097 * @param {Number} currentTimeline 34098 * The last timeline from which a segment was loaded 34099 * @returns {Object} 34100 * A sync-point object 34101 */ 34102 34103 _createClass(SyncController, [{ 34104 key: 'getSyncPoint', 34105 value: function getSyncPoint(playlist, duration, currentTimeline, currentTime) { 34106 var syncPoints = this.runStrategies_(playlist, duration, currentTimeline, currentTime); 34107 34108 if (!syncPoints.length) { 34109 // Signal that we need to attempt to get a sync-point manually 34110 // by fetching a segment in the playlist and constructing 34111 // a sync-point from that information 34112 return null; 34113 } 34114 34115 // Now find the sync-point that is closest to the currentTime because 34116 // that should result in the most accurate guess about which segment 34117 // to fetch 34118 return this.selectSyncPoint_(syncPoints, { key: 'time', value: currentTime }); 34119 } 34120 34121 /** 34122 * Calculate the amount of time that has expired off the playlist during playback 34123 * 34124 * @param {Playlist} playlist 34125 * Playlist object to calculate expired from 34126 * @param {Number} duration 34127 * Duration of the MediaSource (Infinity if playling a live source) 34128 * @returns {Number|null} 34129 * The amount of time that has expired off the playlist during playback. Null 34130 * if no sync-points for the playlist can be found. 34131 */ 34132 }, { 34133 key: 'getExpiredTime', 34134 value: function getExpiredTime(playlist, duration) { 34135 if (!playlist || !playlist.segments) { 34136 return null; 34137 } 34138 34139 var syncPoints = this.runStrategies_(playlist, duration, playlist.discontinuitySequence, 0); 34140
vendor: 10,174 bytes, lines 34141-34432
34141 // Without sync-points, there is not enough information to determine the expired time 34142 if (!syncPoints.length) { 34143 return null; 34144 } 34145 34146 var syncPoint = this.selectSyncPoint_(syncPoints, { 34147 key: 'segmentIndex', 34148 value: 0 34149 }); 34150 34151 // If the sync-point is beyond the start of the playlist, we want to subtract the 34152 // duration from index 0 to syncPoint.segmentIndex instead of adding. 34153 if (syncPoint.segmentIndex > 0) { 34154 syncPoint.time *= -1; 34155 } 34156 34157 return Math.abs(syncPoint.time + (0, _playlist.sumDurations)(playlist, syncPoint.segmentIndex, 0)); 34158 } 34159 34160 /** 34161 * Runs each sync-point strategy and returns a list of sync-points returned by the 34162 * strategies 34163 * 34164 * @private 34165 * @param {Playlist} playlist 34166 * The playlist that needs a sync-point 34167 * @param {Number} duration 34168 * Duration of the MediaSource (Infinity if playing a live source) 34169 * @param {Number} currentTimeline 34170 * The last timeline from which a segment was loaded 34171 * @returns {Array} 34172 * A list of sync-point objects 34173 */ 34174 }, { 34175 key: 'runStrategies_', 34176 value: function runStrategies_(playlist, duration, currentTimeline, currentTime) { 34177 var syncPoints = []; 34178 34179 // Try to find a sync-point in by utilizing various strategies... 34180 for (var i = 0; i < syncPointStrategies.length; i++) { 34181 var strategy = syncPointStrategies[i]; 34182 var syncPoint = strategy.run(this, playlist, duration, currentTimeline, currentTime); 34183 34184 if (syncPoint) { 34185 syncPoint.strategy = strategy.name; 34186 syncPoints.push({ 34187 strategy: strategy.name, 34188 syncPoint: syncPoint 34189 }); 34190 this.logger_('syncPoint found via <' + strategy.name + '>:', syncPoint); 34191 } 34192 } 34193 34194 return syncPoints; 34195 } 34196 34197 /** 34198 * Selects the sync-point nearest the specified target 34199 * 34200 * @private 34201 * @param {Array} syncPoints 34202 * List of sync-points to select from 34203 * @param {Object} target 34204 * Object specifying the property and value we are targeting 34205 * @param {String} target.key 34206 * Specifies the property to target. Must be either 'time' or 'segmentIndex' 34207 * @param {Number} target.value 34208 * The value to target for the specified key. 34209 * @returns {Object} 34210 * The sync-point nearest the target 34211 */ 34212 }, { 34213 key: 'selectSyncPoint_', 34214 value: function selectSyncPoint_(syncPoints, target) { 34215 var bestSyncPoint = syncPoints[0].syncPoint; 34216 var bestDistance = Math.abs(syncPoints[0].syncPoint[target.key] - target.value); 34217 var bestStrategy = syncPoints[0].strategy; 34218 34219 for (var i = 1; i < syncPoints.length; i++) { 34220 var newDistance = Math.abs(syncPoints[i].syncPoint[target.key] - target.value); 34221 34222 if (newDistance < bestDistance) { 34223 bestDistance = newDistance; 34224 bestSyncPoint = syncPoints[i].syncPoint; 34225 bestStrategy = syncPoints[i].strategy; 34226 } 34227 } 34228 34229 this.logger_('syncPoint with strategy <' + bestStrategy + '> chosen: ', bestSyncPoint); 34230 return bestSyncPoint; 34231 } 34232 34233 /** 34234 * Save any meta-data present on the segments when segments leave 34235 * the live window to the playlist to allow for synchronization at the 34236 * playlist level later. 34237 * 34238 * @param {Playlist} oldPlaylist - The previous active playlist 34239 * @param {Playlist} newPlaylist - The updated and most current playlist 34240 */ 34241 }, { 34242 key: 'saveExpiredSegmentInfo', 34243 value: function saveExpiredSegmentInfo(oldPlaylist, newPlaylist) { 34244 var mediaSequenceDiff = newPlaylist.mediaSequence - oldPlaylist.mediaSequence; 34245 34246 // When a segment expires from the playlist and it has a start time 34247 // save that information as a possible sync-point reference in future 34248 for (var i = mediaSequenceDiff - 1; i >= 0; i--) { 34249 var lastRemovedSegment = oldPlaylist.segments[i]; 34250 34251 if (lastRemovedSegment && typeof lastRemovedSegment.start !== 'undefined') { 34252 newPlaylist.syncInfo = { 34253 mediaSequence: oldPlaylist.mediaSequence + i, 34254 time: lastRemovedSegment.start 34255 }; 34256 this.logger_('playlist sync:', newPlaylist.syncInfo); 34257 this.trigger('syncinfoupdate'); 34258 break; 34259 } 34260 } 34261 } 34262 34263 /** 34264 * Save the mapping from playlist's ProgramDateTime to display. This should 34265 * only ever happen once at the start of playback. 34266 * 34267 * @param {Playlist} playlist - The currently active playlist 34268 */ 34269 }, { 34270 key: 'setDateTimeMapping', 34271 value: function setDateTimeMapping(playlist) { 34272 if (!this.datetimeToDisplayTime && playlist.dateTimeObject) { 34273 var playlistTimestamp = playlist.dateTimeObject.getTime() / 1000; 34274 34275 this.datetimeToDisplayTime = -playlistTimestamp; 34276 } 34277 } 34278 34279 /** 34280 * Reset the state of the inspection cache when we do a rendition 34281 * switch 34282 */ 34283 }, { 34284 key: 'reset', 34285 value: function reset() { 34286 this.inspectCache_ = undefined; 34287 } 34288 34289 /** 34290 * Probe or inspect a fmp4 or an mpeg2-ts segment to determine the start 34291 * and end of the segment in it's internal "media time". Used to generate 34292 * mappings from that internal "media time" to the display time that is 34293 * shown on the player. 34294 * 34295 * @param {SegmentInfo} segmentInfo - The current active request information 34296 */ 34297 }, { 34298 key: 'probeSegmentInfo', 34299 value: function probeSegmentInfo(segmentInfo) { 34300 var segment = segmentInfo.segment; 34301 var playlist = segmentInfo.playlist; 34302 var timingInfo = undefined; 34303 34304 if (segment.map) { 34305 timingInfo = this.probeMp4Segment_(segmentInfo); 34306 } else { 34307 timingInfo = this.probeTsSegment_(segmentInfo); 34308 } 34309 34310 if (timingInfo) { 34311 if (this.calculateSegmentTimeMapping_(segmentInfo, timingInfo)) { 34312 this.saveDiscontinuitySyncInfo_(segmentInfo); 34313 34314 // If the playlist does not have sync information yet, record that information 34315 // now with segment timing information 34316 if (!playlist.syncInfo) { 34317 playlist.syncInfo = { 34318 mediaSequence: playlist.mediaSequence + segmentInfo.mediaIndex, 34319 time: segment.start 34320 }; 34321 } 34322 } 34323 } 34324 34325 return timingInfo; 34326 } 34327 34328 /** 34329 * Probe an fmp4 or an mpeg2-ts segment to determine the start of the segment 34330 * in it's internal "media time". 34331 * 34332 * @private 34333 * @param {SegmentInfo} segmentInfo - The current active request information 34334 * @return {object} The start and end time of the current segment in "media time" 34335 */ 34336 }, { 34337 key: 'probeMp4Segment_', 34338 value: function probeMp4Segment_(segmentInfo) { 34339 var segment = segmentInfo.segment; 34340 var timescales = _muxJsLibMp4Probe2['default'].timescale(segment.map.bytes); 34341 var startTime = _muxJsLibMp4Probe2['default'].startTime(timescales, segmentInfo.bytes); 34342 34343 if (segmentInfo.timestampOffset !== null) { 34344 segmentInfo.timestampOffset -= startTime; 34345 } 34346 34347 return { 34348 start: startTime, 34349 end: startTime + segment.duration 34350 }; 34351 } 34352 34353 /** 34354 * Probe an mpeg2-ts segment to determine the start and end of the segment 34355 * in it's internal "media time". 34356 * 34357 * @private 34358 * @param {SegmentInfo} segmentInfo - The current active request information 34359 * @return {object} The start and end time of the current segment in "media time" 34360 */ 34361 }, { 34362 key: 'probeTsSegment_', 34363 value: function probeTsSegment_(segmentInfo) { 34364 var timeInfo = (0, _muxJsLibToolsTsInspectorJs.inspect)(segmentInfo.bytes, this.inspectCache_); 34365 var segmentStartTime = undefined; 34366 var segmentEndTime = undefined; 34367 34368 if (!timeInfo) { 34369 return null; 34370 } 34371 34372 if (timeInfo.video && timeInfo.video.length === 2) { 34373 this.inspectCache_ = timeInfo.video[1].dts; 34374 segmentStartTime = timeInfo.video[0].dtsTime; 34375 segmentEndTime = timeInfo.video[1].dtsTime; 34376 } else if (timeInfo.audio && timeInfo.audio.length === 2) { 34377 this.inspectCache_ = timeInfo.audio[1].dts; 34378 segmentStartTime = timeInfo.audio[0].dtsTime; 34379 segmentEndTime = timeInfo.audio[1].dtsTime; 34380 } 34381 34382 return { 34383 start: segmentStartTime, 34384 end: segmentEndTime, 34385 containsVideo: timeInfo.video && timeInfo.video.length === 2, 34386 containsAudio: timeInfo.audio && timeInfo.audio.length === 2 34387 }; 34388 } 34389 }, { 34390 key: 'timestampOffsetForTimeline', 34391 value: function timestampOffsetForTimeline(timeline) { 34392 if (typeof this.timelines[timeline] === 'undefined') { 34393 return null; 34394 } 34395 return this.timelines[timeline].time; 34396 } 34397 34398 /** 34399 * Use the "media time" for a segment to generate a mapping to "display time" and 34400 * save that display time to the segment. 34401 * 34402 * @private 34403 * @param {SegmentInfo} segmentInfo 34404 * The current active request information 34405 * @param {object} timingInfo 34406 * The start and end time of the current segment in "media time" 34407 * @returns {Boolean} 34408 * Returns false if segment time mapping could not be calculated 34409 */ 34410 }, { 34411 key: 'calculateSegmentTimeMapping_', 34412 value: function calculateSegmentTimeMapping_(segmentInfo, timingInfo) { 34413 var segment = segmentInfo.segment; 34414 var mappingObj = this.timelines[segmentInfo.timeline]; 34415 34416 if (segmentInfo.timestampOffset !== null) { 34417 this.logger_('tsO:', segmentInfo.timestampOffset); 34418 34419 mappingObj = { 34420 time: segmentInfo.startOfSegment, 34421 mapping: segmentInfo.startOfSegment - timingInfo.start 34422 }; 34423 this.timelines[segmentInfo.timeline] = mappingObj; 34424 this.trigger('timestampoffset'); 34425 34426 segment.start = segmentInfo.startOfSegment; 34427 segment.end = timingInfo.end + mappingObj.mapping; 34428 } else if (mappingObj) { 34429 segment.start = timingInfo.start + mappingObj.mapping; 34430 segment.end = timingInfo.end + mappingObj.mapping; 34431 } else { 34432 return false;
34433 } 34434 34435 return true; 34436 } 34437 34438 /** 34439 * Each time we have discontinuity in the playlist, attempt to calculate the location 34440 * in display of the start of the discontinuity and save that. We also save an accuracy 34441 * value so that we save values with the most accuracy (closest to 0.) 34442 * 34443 * @private 34444 * @param {SegmentInfo} segmentInfo - The current active request information 34445 */ 34446 }, { 34447 key: 'saveDiscontinuitySyncInfo_', 34448 value: function saveDiscontinuitySyncInfo_(segmentInfo) { 34449 var playlist = segmentInfo.playlist; 34450 var segment = segmentInfo.segment; 34451 34452 // If the current segment is a discontinuity then we know exactly where 34453 // the start of the range and it's accuracy is 0 (greater accuracy values 34454 // mean more approximation) 34455 if (segment.discontinuity) { 34456 this.discontinuities[segment.timeline] = { 34457 time: segment.start, 34458 accuracy: 0 34459 }; 34460 } else if (playlist.discontinuityStarts.length) { 34461 // Search for future discontinuities that we can provide better timing 34462 // information for and save that information for sync purposes 34463 for (var i = 0; i < playlist.discontinuityStarts.length; i++) { 34464 var segmentIndex = playlist.discontinuityStarts[i]; 34465 var discontinuity = playlist.discontinuitySequence + i + 1; 34466 var mediaIndexDiff = segmentIndex - segmentInfo.mediaIndex; 34467 var accuracy = Math.abs(mediaIndexDiff); 34468 34469 if (!this.discontinuities[discontinuity] || this.discontinuities[discontinuity].accuracy > accuracy) { 34470 var time = undefined; 34471 34472 if (mediaIndexDiff < 0) { 34473 time = segment.start - (0, _playlist.sumDurations)(playlist, segmentInfo.mediaIndex, segmentIndex); 34474 } else { 34475 time = segment.end + (0, _playlist.sumDurations)(playlist, segmentInfo.mediaIndex + 1, segmentIndex); 34476 } 34477 34478 this.discontinuities[discontinuity] = { 34479 time: time, 34480 accuracy: accuracy 34481 }; 34482 } 34483 } 34484 } 34485 } 34486 34487 /** 34488 * A debugging logger noop that is set to console.log only if debugging 34489 * is enabled globally 34490 * 34491 * @private 34492 */ 34493 }, { 34494 key: 'logger_', 34495 value: function logger_() {} 34496 }]); 34497 34498 return SyncController; 34499})(_videoJs2['default'].EventTarget); 34500 34501exports['default'] = SyncController; 34502}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 34503},{"./playlist":11,"mux.js/lib/mp4/probe":57,"mux.js/lib/tools/ts-inspector.js":59}],19:[function(require,module,exports){ 34504 34505/** 34506 * @file - codecs.js - Handles tasks regarding codec strings such as translating them to 34507 * codec strings, or translating codec strings into objects that can be examined. 34508 */ 34509 34510/** 34511 * Parses a codec string to retrieve the number of codecs specified, 34512 * the video codec and object type indicator, and the audio profile. 34513 */ 34514 34515'use strict'; 34516 34517Object.defineProperty(exports, '__esModule', { 34518 value: true 34519}); 34520var parseCodecs = function parseCodecs() { 34521 var codecs = arguments.length <= 0 || arguments[0] === undefined ? '' : arguments[0]; 34522 34523 var result = { 34524 codecCount: 0 34525 }; 34526 var parsed = undefined; 34527 34528 result.codecCount = codecs.split(',').length; 34529 result.codecCount = result.codecCount || 2; 34530 34531 // parse the video codec 34532 parsed = /(^|\s|,)+(avc1)([^ ,]*)/i.exec(codecs); 34533 if (parsed) { 34534 result.videoCodec = parsed[2]; 34535 result.videoObjectTypeIndicator = parsed[3]; 34536 } 34537 34538 // parse the last field of the audio codec 34539 result.audioProfile = /(^|\s|,)+mp4a.[0-9A-Fa-f]+\.([0-9A-Fa-f]+)/i.exec(codecs); 34540 result.audioProfile = result.audioProfile && result.audioProfile[2]; 34541 34542 return result; 34543}; 34544exports.parseCodecs = parseCodecs; 34545},{}],20:[function(require,module,exports){ 34546(function (global){ 34547/** 34548 * @file vtt-segment-loader.js 34549 */ 34550'use strict'; 34551 34552Object.defineProperty(exports, '__esModule', { 34553 value: true 34554}); 34555 34556var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 34557 34558var _get = function get(_x3, _x4, _x5) { var _again = true; _function: while (_again) { var object = _x3, property = _x4, receiver = _x5; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x3 = parent; _x4 = property; _x5 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 34559 34560function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 34561 34562function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 34563 34564function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
34564onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 34565 34566var _segmentLoader = require('./segment-loader'); 34567 34568var _segmentLoader2 = _interopRequireDefault(_segmentLoader); 34569 34570var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null); 34571 34572var _videoJs2 = _interopRequireDefault(_videoJs); 34573 34574var _globalWindow = require('global/window'); 34575 34576var _globalWindow2 = _interopRequireDefault(_globalWindow); 34577 34578var _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs = require('videojs-contrib-media-sources/es5/remove-cues-from-track.js'); 34579 34580var _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs2 = _interopRequireDefault(_videojsContribMediaSourcesEs5RemoveCuesFromTrackJs); 34581 34582var _binUtils = require('./bin-utils'); 34583 34584var VTT_LINE_TERMINATORS = new Uint8Array('\n\n'.split('').map(function (char) { 34585 return char.charCodeAt(0); 34586})); 34587 34588var uintToString = function uintToString(uintArray) { 34589 return String.fromCharCode.apply(null, uintArray); 34590}; 34591 34592/** 34593 * An object that manages segment loading and appending. 34594 * 34595 * @class VTTSegmentLoader 34596 * @param {Object} options required and optional options 34597 * @extends videojs.EventTarget 34598 */ 34599 34600var VTTSegmentLoader = (function (_SegmentLoader) { 34601 _inherits(VTTSegmentLoader, _SegmentLoader); 34602 34603 function VTTSegmentLoader(settings) { 34604 var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; 34605 34606 _classCallCheck(this, VTTSegmentLoader); 34607 34608 _get(Object.getPrototypeOf(VTTSegmentLoader.prototype), 'constructor', this).call(this, settings, options); 34609 34610 // SegmentLoader requires a MediaSource be specified or it will throw an error; 34611 // however, VTTSegmentLoader has no need of a media source, so delete the reference 34612 this.mediaSource_ = null; 34613 34614 this.subtitlesTrack_ = null; 34615 } 34616 34617 /** 34618 * Indicates which time ranges are buffered 34619 * 34620 * @return {TimeRange} 34621 * TimeRange object representing the current buffered ranges 34622 */ 34623 34624 _createClass(VTTSegmentLoader, [{ 34625 key: 'buffered_', 34626 value: function buffered_() { 34627 if (!this.subtitlesTrack_ || !this.subtitlesTrack_.cues.length) { 34628 return _videoJs2['default'].createTimeRanges(); 34629 } 34630 34631 var cues = this.subtitlesTrack_.cues; 34632 var start = cues[0].startTime; 34633 var end = cues[cues.length - 1].startTime; 34634 34635 return _videoJs2['default'].createTimeRanges([[start, end]]); 34636 } 34637 34638 /** 34639 * Gets and sets init segment for the provided map 34640 * 34641 * @param {Object} map 34642 * The map object representing the init segment to get or set 34643 * @param {Boolean=} set 34644 * If true, the init segment for the provided map should be saved 34645 * @return {Object} 34646 * map object for desired init segment 34647 */ 34648 }, { 34649 key: 'initSegment', 34650 value: function initSegment(map) { 34651 var set = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; 34652 34653 if (!map) { 34654 return null; 34655 } 34656 34657 var id = (0, _binUtils.initSegmentId)(map); 34658 var storedMap = this.initSegments_[id]; 34659 34660 if (set && !storedMap && map.bytes) { 34661 // append WebVTT line terminators to the media initialization segment if it exists 34662 // to follow the WebVTT spec (https://w3c.github.io/webvtt/#file-structure) that 34663 // requires two or more WebVTT line terminators between the WebVTT header and the 34664 // rest of the file 34665 var combinedByteLength = VTT_LINE_TERMINATORS.byteLength + map.bytes.byteLength; 34666 var combinedSegment = new Uint8Array(combinedByteLength); 34667 34668 combinedSegment.set(map.bytes); 34669 combinedSegment.set(VTT_LINE_TERMINATORS, map.bytes.byteLength); 34670 34671 this.initSegments_[id] = storedMap = { 34672 resolvedUri: map.resolvedUri, 34673 byterange: map.byterange, 34674 bytes: combinedSegment 34675 }; 34676 } 34677 34678 return storedMap || map; 34679 } 34680 34681 /** 34682 * Returns true if all configuration required for loading is present, otherwise false. 34683 * 34684 * @return {Boolean} True if the all configuration is ready for loading 34685 * @private 34686 */ 34687 }, { 34688 key: 'couldBeginLoading_', 34689 value: function couldBeginLoading_() { 34690 return this.playlist_ && this.subtitlesTrack_ && !this.paused(); 34691 } 34692 34693 /** 34694 * Once all the starting parameters have been specified, begin 34695 * operation. This method should only be invoked from the INIT 34696 * state. 34697 * 34698 * @private 34699 */ 34700 }, { 34701 key: 'init_', 34702 value: function init_() { 34703 this.state = 'READY'; 34704 this.resetEverything(); 34705 return this.monitorBuffer_(); 34706 } 34707 34708 /** 34709 * Set a subtitle track on the segment loader to add subtitles to 34710 * 34711 * @param {TextTrack=} track 34712 * The text track to add loaded subtitles to 34713 * @return {TextTrack} 34714 * Returns the subtitles track 34715 */ 34716 }, { 34717 key: 'track', 34718 value: function track(_track) { 34719 if (typeof _track === 'undefined') { 34720 return this.subtitlesTrack_; 34721 } 34722 34723 this.subtitlesTrack_ = _track; 34724 34725 // if we were unpaused but waiting for a sourceUpdater, start 34726 // buffering now 34727 if (this.state === 'INIT' && this.couldBeginLoading_()) { 34728 this.init_(); 34729 } 34730 34731 return this.subtitlesTrack_; 34732 } 34733 34734 /** 34735 * Remove any data in the source buffer between start and end times 34736 * @param {Number} start - the start time of the region to remove from the buffer 34737 * @param {Number} end - the end time of the region to remove from the buffer 34738 */ 34739 }, { 34740 key: 'remove', 34741 value: function remove(start, end) { 34742 (0, _videojsContribMediaSourcesEs5RemoveCuesFromTrackJs2['default'])(start, end, this.subtitlesTrack_); 34743 } 34744 34745 /** 34746 * fill the buffer with segements unless the sourceBuffers are 34747 * currently updating 34748 * 34749 * Note: this function should only ever be called by monitorBuffer_ 34750 * and never directly 34751 * 34752 * @private 34753 */ 34754 }, { 34755 key: 'fillBuffer_', 34756 value: function fillBuffer_() { 34757 var _this = this; 34758 34759 if (!this.syncPoint_) { 34760 this.syncPoint_ = this.syncController_.getSyncPoint(this.playlist_, this.duration_(), this.currentTimeline_, this.currentTime_()); 34761 } 34762 34763 // see if we need to begin loading immediately 34764 var segmentInfo = this.checkBuffer_(this.buffered_(), this.playlist_, this.mediaIndex, this.hasPlayed_(), this.currentTime_(), this.syncPoint_); 34765 34766 segmentInfo = this.skipEmptySegments_(segmentInfo); 34767 34768 if (!segmentInfo) { 34769 return; 34770 } 34771 34772 if (this.syncController_.timestampOffsetForTimeline(segmentInfo.timeline) === null) { 34773 // We don't have the timestamp offset that we need to sync subtitles. 34774 // Rerun on a timestamp offset or user interaction. 34775 var checkTimestampOffset = function checkTimestampOffset() { 34776 _this.state = 'READY'; 34777 if (!_this.paused()) { 34778 // if not paused, queue a buffer check as soon as possible 34779 _this.monitorBuffer_(); 34780 } 34781 }; 34782 34783 this.syncController_.one('timestampoffset', checkTimestampOffset); 34784 this.state = 'WAITING_ON_TIMELINE'; 34785 return; 34786 } 34787 34788 this.loadSegment_(segmentInfo); 34789 } 34790 34791 /** 34792 * Prevents the segment loader from requesting segments we know contain no subtitles 34793 * by walking forward until we find the next segment that we don't know
34793whether it is 34794 * empty or not. 34795 * 34796 * @param {Object} segmentInfo 34797 * a segment info object that describes the current segment 34798 * @return {Object} 34799 * a segment info object that describes the current segment 34800 */ 34801 }, { 34802 key: 'skipEmptySegments_', 34803 value: function skipEmptySegments_(segmentInfo) { 34804 while (segmentInfo && segmentInfo.segment.empty) { 34805 segmentInfo = this.generateSegmentInfo_(segmentInfo.playlist, segmentInfo.mediaIndex + 1, segmentInfo.startOfSegment + segmentInfo.duration, segmentInfo.isSyncRequest); 34806 } 34807 return segmentInfo; 34808 } 34809 34810 /** 34811 * append a decrypted segement to the SourceBuffer through a SourceUpdater 34812 * 34813 * @private 34814 */ 34815 }, { 34816 key: 'handleSegment_', 34817 value: function handleSegment_() { 34818 var _this2 = this; 34819 34820 if (!this.pendingSegment_ || !this.subtitlesTrack_) { 34821 this.state = 'READY'; 34822 return; 34823 } 34824 34825 this.state = 'APPENDING'; 34826 34827 var segmentInfo = this.pendingSegment_; 34828 var segment = segmentInfo.segment; 34829 34830 // Make sure that vttjs has loaded, otherwise, wait till it finished loading 34831 if (typeof _globalWindow2['default'].WebVTT !== 'function' && this.subtitlesTrack_ && this.subtitlesTrack_.tech_) { 34832 var _ret = (function () { 34833 34834 var loadHandler = function loadHandler() { 34835 _this2.handleSegment_(); 34836 }; 34837 34838 _this2.state = 'WAITING_ON_VTTJS'; 34839 _this2.subtitlesTrack_.tech_.one('vttjsloaded', loadHandler); 34840 _this2.subtitlesTrack_.tech_.one('vttjserror', function () { 34841 _this2.subtitlesTrack_.tech_.off('vttjsloaded', loadHandler); 34842 _this2.error({ 34843 message: 'Error loading vtt.js' 34844 }); 34845 _this2.state = 'READY'; 34846 _this2.pause(); 34847 _this2.trigger('error'); 34848 }); 34849 34850 return { 34851 v: undefined 34852 }; 34853 })(); 34854 34855 if (typeof _ret === 'object') return _ret.v; 34856 } 34857 34858 segment.requested = true; 34859 34860 try { 34861 this.parseVTTCues_(segmentInfo); 34862 } catch (e) { 34863 this.error({ 34864 message: e.message 34865 }); 34866 this.state = 'READY'; 34867 this.pause(); 34868 return this.trigger('error'); 34869 } 34870 34871 this.updateTimeMapping_(segmentInfo, this.syncController_.timelines[segmentInfo.timeline], this.playlist_); 34872 34873 if (segmentInfo.isSyncRequest) { 34874 this.trigger('syncinfoupdate'); 34875 this.pendingSegment_ = null; 34876 this.state = 'READY'; 34877 return; 34878 } 34879 34880 segmentInfo.byteLength = segmentInfo.bytes.byteLength; 34881 34882 this.mediaSecondsLoaded += segment.duration; 34883 34884 if (segmentInfo.cues.length) { 34885 // remove any overlapping cues to prevent doubling 34886 this.remove(segmentInfo.cues[0].endTime, segmentInfo.cues[segmentInfo.cues.length - 1].endTime); 34887 } 34888 34889 segmentInfo.cues.forEach(function (cue) { 34890 _this2.subtitlesTrack_.addCue(cue); 34891 }); 34892 34893 this.handleUpdateEnd_(); 34894 } 34895 34896 /** 34897 * Uses the WebVTT parser to parse the segment response 34898 * 34899 * @param {Object} segmentInfo 34900 * a segment info object that describes the current segment 34901 * @private 34902 */ 34903 }, { 34904 key: 'parseVTTCues_', 34905 value: function parseVTTCues_(segmentInfo) { 34906 var decoder = undefined; 34907 var decodeBytesToString = false; 34908 34909 if (typeof _globalWindow2['default'].TextDecoder === 'function') { 34910 decoder = new _globalWindow2['default'].TextDecoder('utf8'); 34911 } else { 34912 decoder = _globalWindow2['default'].WebVTT.StringDecoder(); 34913 decodeBytesToString = true; 34914 } 34915 34916 var parser = new _globalWindow2['default'].WebVTT.Parser(_globalWindow2['default'], _globalWindow2['default'].vttjs, decoder); 34917 34918 segmentInfo.cues = []; 34919 segmentInfo.timestampmap = { MPEGTS: 0, LOCAL: 0 }; 34920 34921 parser.oncue = segmentInfo.cues.push.bind(segmentInfo.cues); 34922 parser.ontimestampmap = function (map) { 34923 return segmentInfo.timestampmap = map; 34924 }; 34925 parser.onparsingerror = function (error) { 34926 _videoJs2['default'].log.warn('Error encountered when parsing cues: ' + error.message); 34927 }; 34928 34929 if (segmentInfo.segment.map) { 34930 var mapData = segmentInfo.segment.map.bytes; 34931 34932 if (decodeBytesToString) { 34933 mapData = uintToString(mapData); 34934 } 34935 34936 parser.parse(mapData); 34937 } 34938 34939 var segmentData = segmentInfo.bytes; 34940 34941 if (decodeBytesToString) { 34942 segmentData = uintToString(segmentData); 34943 } 34944 34945 parser.parse(segmentData); 34946 parser.flush(); 34947 } 34948 34949 /** 34950 * Updates the start and end times of any cues parsed by the WebVTT parser using 34951 * the information parsed from the X-TIMESTAMP-MAP header and a TS to media time mapping 34952 * from the SyncController 34953 * 34954 * @param {Object} segmentInfo 34955 * a segment info object that describes the current segment 34956 * @param {Object} mappingObj
34957 * object containing a mapping from TS to media time 34958 * @param {Object} playlist 34959 * the playlist object containing the segment 34960 * @private 34961 */ 34962 }, { 34963 key: 'updateTimeMapping_', 34964 value: function updateTimeMapping_(segmentInfo, mappingObj, playlist) { 34965 var segment = segmentInfo.segment; 34966 34967 if (!mappingObj) { 34968 // If the sync controller does not have a mapping of TS to Media Time for the 34969 // timeline, then we don't have enough information to update the cue 34970 // start/end times 34971 return; 34972 } 34973 34974 if (!segmentInfo.cues.length) { 34975 // If there are no cues, we also do not have enough information to figure out 34976 // segment timing. Mark that the segment contains no cues so we don't re-request 34977 // an empty segment. 34978 segment.empty = true; 34979 return; 34980 } 34981 34982 var timestampmap = segmentInfo.timestampmap; 34983 var diff = timestampmap.MPEGTS / 90000 - timestampmap.LOCAL + mappingObj.mapping; 34984 34985 segmentInfo.cues.forEach(function (cue) { 34986 // First convert cue time to TS time using the timestamp-map provided within the vtt 34987 cue.startTime += diff; 34988 cue.endTime += diff; 34989 }); 34990 34991 if (!playlist.syncInfo) { 34992 var firstStart = segmentInfo.cues[0].startTime; 34993 var lastStart = segmentInfo.cues[segmentInfo.cues.length - 1].startTime; 34994 34995 playlist.syncInfo = { 34996 mediaSequence: playlist.mediaSequence + segmentInfo.mediaIndex, 34997 time: Math.min(firstStart, lastStart - segment.duration) 34998 }; 34999 } 35000 } 35001 }]); 35002 35003 return VTTSegmentLoader; 35004})(_segmentLoader2['default']); 35005 35006exports['default'] = VTTSegmentLoader; 35007module.exports = exports['default']; 35008}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 35009},{"./bin-utils":2,"./segment-loader":16,"global/window":32,"videojs-contrib-media-sources/es5/remove-cues-from-track.js":72}],21:[function(require,module,exports){ 35010(function (global){ 35011/** 35012 * @file xhr.js 35013 */ 35014 35015/** 35016 * A wrapper for videojs.xhr that tracks bandwidth. 35017 * 35018 * @param {Object} options options for the XHR 35019 * @param {Function} callback the callback to call when done 35020 * @return {Request} the xhr request that is going to be made 35021 */ 35022'use strict'; 35023 35024Object.defineProperty(exports, '__esModule', { 35025 value: true 35026}); 35027 35028function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 35029 35030var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null); 35031 35032var _videoJs2 = _interopRequireDefault(_videoJs); 35033 35034var xhrFactory = function xhrFactory() { 35035 var xhr = function XhrFunction(options, callback) { 35036 // Add a default timeout for all hls requests 35037 options = (0, _videoJs.mergeOptions)({ 35038 timeout: 45e3 35039 }, options); 35040 35041 // Allow an optional user-specified function to modify the option 35042 // object before we construct the xhr request 35043 var beforeRequest = XhrFunction.beforeRequest || _videoJs2['default'].Hls.xhr.beforeRequest; 35044 35045 if (beforeRequest && typeof beforeRequest === 'function') { 35046 var newOptions = beforeRequest(options); 35047 35048 if (newOptions) { 35049 options = newOptions; 35050 } 35051 } 35052 35053 var request = (0, _videoJs.xhr)(options, function (error, response) { 35054 var reqResponse = request.response; 35055 35056 if (!error && reqResponse) { 35057 request.responseTime = Date.now(); 35058 request.roundTripTime = request.responseTime - request.requestTime; 35059 request.bytesReceived = reqResponse.byteLength || reqResponse.length; 35060 if (!request.bandwidth) { 35061 request.bandwidth = Math.floor(request.bytesReceived / request.roundTripTime * 8 * 1000); 35062 } 35063 } 35064 35065 // videojs.xhr now uses a specific code on the error 35066 // object to signal that a request has timed out instead 35067 // of setting a boolean on the request object 35068 if (error && error.code === 'ETIMEDOUT') { 35069 request.timedout = true; 35070 } 35071 35072 // videojs.xhr no longer considers status codes outside of 200 and 0 35073 // (for file uris) to be errors, but the old XHR did, so emulate that 35074 // behavior. Status 206 may be used in response to byterange requests. 35075 if (!error && !request.aborted && response.statusCode !== 200 && response.statusCode !== 206 && response.statusCode !== 0) { 35076 error = new Error('XHR Failed with a response of: ' + (request && (reqResponse || request.responseText))); 35077 } 35078 35079 callback(error, request); 35080 }); 35081 var originalAbort = request.abort; 35082 35083 request.abort = function () { 35084 request.aborted = true; 35085 return originalAbort.apply(request, arguments); 35086 }; 35087 request.uri = options.uri; 35088 request.requestTime = Date.now(); 35089 return request; 35090 }; 35091 35092 return xhr; 35093}; 35094 35095exports['default'] = xhrFactory; 35096module.exports = exports['default']; 35097}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 35098},{}],22:[function(require,module,exports){ 35099/** 35100 * @file aes.js 35101 * 35102 * This file contains an adaptation of the AES decryption algorithm 35103 * from the Standford Javascript Cryptography Library. That work is 35104 * covered by the following copyright and permissions notice: 35105 * 35106 * Copyright 2009-2010 Emily Stark, Mike Hamburg, Dan Boneh. 35107 * All rights reserved. 35108 * 35109 * Redistribution and use in source and binary forms, with or without 35110 * modification, are permitted provided that the following conditions are 35111 * met: 35112 * 35113 * 1. Redistributions of source code must retain the above copyright 35114 * notice, this list of conditions and the following disclaimer. 35115 * 35116 * 2. Redistributions in binary form must reproduce the above 35117 * copyright notice, this list of conditions and the following 35118 * disclaimer in the documentation and/or other materials provided 35119 * with the distribution. 35120 * 35121 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 35122 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 35123 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 35124 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR CONTRIBUTORS BE 35125 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
35126 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 35127 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 35128 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 35129 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 35130 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 35131 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35132 * 35133 * The views and conclusions contained in the software and documentation 35134 * are those of the authors and should not be interpreted as representing 35135 * official policies, either expressed or implied, of the authors. 35136 */ 35137 35138/** 35139 * Expand the S-box tables. 35140 * 35141 * @private 35142 */ 35143'use strict'; 35144 35145Object.defineProperty(exports, '__esModule', { 35146 value: true 35147}); 35148 35149var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 35150 35151function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 35152 35153var precompute = function precompute() { 35154 var tables = [[[], [], [], [], []], [[], [], [], [], []]]; 35155 var encTable = tables[0]; 35156 var decTable = tables[1]; 35157 var sbox = encTable[4]; 35158 var sboxInv = decTable[4]; 35159 var i = undefined; 35160 var x = undefined; 35161 var xInv = undefined; 35162 var d = []; 35163 var th = []; 35164 var x2 = undefined; 35165 var x4 = undefined; 35166 var x8 = undefined; 35167 var s = undefined; 35168 var tEnc = undefined; 35169 var tDec = undefined; 35170 35171 // Compute double and third tables 35172 for (i = 0; i < 256; i++) { 35173 th[(d[i] = i << 1 ^ (i >> 7) * 283) ^ i] = i; 35174 } 35175 35176 for (x = xInv = 0; !sbox[x]; x ^= x2 || 1, xInv = th[xInv] || 1) { 35177 // Compute sbox 35178 s = xInv ^ xInv << 1 ^ xInv << 2 ^ xInv << 3 ^ xInv << 4; 35179 s = s >> 8 ^ s & 255 ^ 99; 35180 sbox[x] = s; 35181 sboxInv[s] = x; 35182 35183 // Compute MixColumns 35184 x8 = d[x4 = d[x2 = d[x]]]; 35185 tDec = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100; 35186 tEnc = d[s] * 0x101 ^ s * 0x1010100; 35187 35188 for (i = 0; i < 4; i++) { 35189 encTable[i][x] = tEnc = tEnc << 24 ^ tEnc >>> 8; 35190 decTable[i][s] = tDec = tDec << 24 ^ tDec >>> 8; 35191 } 35192 } 35193 35194 // Compactify. Considerable speedup on Firefox. 35195 for (i = 0; i < 5; i++) { 35196 encTable[i] = encTable[i].slice(0); 35197 decTable[i] = decTable[i].slice(0); 35198 } 35199 return tables; 35200}; 35201var aesTables = null; 35202 35203/** 35204 * Schedule out an AES key for both encryption and decryption. This 35205 * is a low-level class. Use a cipher mode to do bulk encryption. 35206 * 35207 * @class AES 35208 * @param key {Array} The key as an array of 4, 6 or 8 words. 35209 */ 35210 35211var AES = (function () { 35212 function AES(key) { 35213 _classCallCheck(this, AES); 35214 35215 /** 35216 * The expanded S-box and inverse S-box tables. These will be computed 35217 * on the client so that we don't have to send them down the wire. 35218 * 35219 * There are two tables, _tables[0] is for encryption and 35220 * _tables[1] is for decryption. 35221 * 35222 * The first 4 sub-tables are the expanded S-box with MixColumns. The 35223 * last (_tables[01][4]) is the S-box itself. 35224 * 35225 * @private 35226 */ 35227 // if we have yet to precompute the S-box tables 35228 // do so now 35229 if (!aesTables) { 35230 aesTables = precompute(); 35231 } 35232 // then make a copy of that object for use 35233 this._tables = [[aesTables[0][0].slice(), aesTables[0][1].slice(), aesTables[0][2].slice(), aesTables[0][3].slice(), aesTables[0][4].slice()], [aesTables[1][0].slice(), aesTables[1][1].slice(), aesTables[1][2].slice(), aesTables[1][3].slice(), aesTables[1][4].slice()]]; 35234 var i = undefined; 35235 var j = undefined; 35236 var tmp = undefined; 35237 var encKey = undefined; 35238 var decKey = undefined; 35239 var sbox = this._tables[0][4]; 35240 var decTable = this._tables[1]; 35241 var keyLen = key.length; 35242 var rcon = 1; 35243 35244 if (keyLen !== 4 && keyLen !== 6 && keyLen !== 8) { 35245 throw new Error('Invalid aes key size'); 35246 } 35247 35248 encKey = key.slice(0); 35249 decKey = []; 35250 this._key = [encKey, decKey]; 35251 35252 // schedule encryption keys 35253 for (i = keyLen; i < 4 * keyLen + 28; i++) { 35254 tmp = encKey[i - 1]; 35255 35256 // apply sbox
35257 if (i % keyLen === 0 || keyLen === 8 && i % keyLen === 4) { 35258 tmp = sbox[tmp >>> 24] << 24 ^ sbox[tmp >> 16 & 255] << 16 ^ sbox[tmp >> 8 & 255] << 8 ^ sbox[tmp & 255]; 35259 35260 // shift rows and add rcon 35261 if (i % keyLen === 0) { 35262 tmp = tmp << 8 ^ tmp >>> 24 ^ rcon << 24; 35263 rcon = rcon << 1 ^ (rcon >> 7) * 283; 35264 } 35265 } 35266 35267 encKey[i] = encKey[i - keyLen] ^ tmp; 35268 } 35269 35270 // schedule decryption keys 35271 for (j = 0; i; j++, i--) { 35272 tmp = encKey[j & 3 ? i : i - 4]; 35273 if (i <= 4 || j < 4) { 35274 decKey[j] = tmp; 35275 } else { 35276 decKey[j] = decTable[0][sbox[tmp >>> 24]] ^ decTable[1][sbox[tmp >> 16 & 255]] ^ decTable[2][sbox[tmp >> 8 & 255]] ^ decTable[3][sbox[tmp & 255]]; 35277 } 35278 } 35279 } 35280 35281 /** 35282 * Decrypt 16 bytes, specified as four 32-bit words. 35283 * 35284 * @param {Number} encrypted0 the first word to decrypt 35285 * @param {Number} encrypted1 the second word to decrypt 35286 * @param {Number} encrypted2 the third word to decrypt 35287 * @param {Number} encrypted3 the fourth word to decrypt 35288 * @param {Int32Array} out the array to write the decrypted words 35289 * into 35290 * @param {Number} offset the offset into the output array to start 35291 * writing results 35292 * @return {Array} The plaintext. 35293 */ 35294 35295 _createClass(AES, [{ 35296 key: 'decrypt', 35297 value: function decrypt(encrypted0, encrypted1, encrypted2, encrypted3, out, offset) { 35298 var key = this._key[1]; 35299 // state variables a,b,c,d are loaded with pre-whitened data 35300 var a = encrypted0 ^ key[0]; 35301 var b = encrypted3 ^ key[1]; 35302 var c = encrypted2 ^ key[2]; 35303 var d = encrypted1 ^ key[3]; 35304 var a2 = undefined; 35305 var b2 = undefined; 35306 var c2 = undefined; 35307 35308 // key.length === 2 ? 35309 var nInnerRounds = key.length / 4 - 2; 35310 var i = undefined; 35311 var kIndex = 4; 35312 var table = this._tables[1]; 35313 35314 // load up the tables 35315 var table0 = table[0]; 35316 var table1 = table[1]; 35317 var table2 = table[2]; 35318 var table3 = table[3]; 35319 var sbox = table[4]; 35320 35321 // Inner rounds. Cribbed from OpenSSL. 35322 for (i = 0; i < nInnerRounds; i++) { 35323 a2 = table0[a >>> 24] ^ table1[b >> 16 & 255] ^ table2[c >> 8 & 255] ^ table3[d & 255] ^ key[kIndex]; 35324 b2 = table0[b >>> 24] ^ table1[c >> 16 & 255] ^ table2[d >> 8 & 255] ^ table3[a & 255] ^ key[kIndex + 1]; 35325 c2 = table0[c >>> 24] ^ table1[d >> 16 & 255] ^ table2[a >> 8 & 255] ^ table3[b & 255] ^ key[kIndex + 2]; 35326 d = table0[d >>> 24] ^ table1[a >> 16 & 255] ^ table2[b >> 8 & 255] ^ table3[c & 255] ^ key[kIndex + 3]; 35327 kIndex += 4; 35328 a = a2;b = b2;c = c2; 35329 } 35330 35331 // Last round. 35332 for (i = 0; i < 4; i++) { 35333 out[(3 & -i) + offset] = sbox[a >>> 24] << 24 ^ sbox[b >> 16 & 255] << 16 ^ sbox[c >> 8 & 255] << 8 ^ sbox[d & 255] ^ key[kIndex++]; 35334 a2 = a;a = b;b = c;c = d;d = a2; 35335 } 35336 } 35337 }]); 35338 35339 return AES; 35340})(); 35341 35342exports['default'] = AES; 35343module.exports = exports['default']; 35344},{}],23:[function(require,module,exports){ 35345/** 35346 * @file async-stream.js 35347 */ 35348'use strict'; 35349 35350Object.defineProperty(exports, '__esModule', { 35351 value: true 35352}); 35353 35354var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 35355 35356var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 35357 35358function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 35359 35360function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 35361 35362function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
35362onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 35363 35364var _stream = require('./stream'); 35365 35366var _stream2 = _interopRequireDefault(_stream); 35367 35368/** 35369 * A wrapper around the Stream class to use setTiemout 35370 * and run stream "jobs" Asynchronously 35371 * 35372 * @class AsyncStream 35373 * @extends Stream 35374 */ 35375 35376var AsyncStream = (function (_Stream) { 35377 _inherits(AsyncStream, _Stream); 35378 35379 function AsyncStream() { 35380 _classCallCheck(this, AsyncStream); 35381 35382 _get(Object.getPrototypeOf(AsyncStream.prototype), 'constructor', this).call(this, _stream2['default']); 35383 this.jobs = []; 35384 this.delay = 1; 35385 this.timeout_ = null; 35386 } 35387 35388 /** 35389 * process an async job 35390 * 35391 * @private 35392 */ 35393 35394 _createClass(AsyncStream, [{ 35395 key: 'processJob_', 35396 value: function processJob_() { 35397 this.jobs.shift()(); 35398 if (this.jobs.length) { 35399 this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay); 35400 } else { 35401 this.timeout_ = null; 35402 } 35403 } 35404 35405 /** 35406 * push a job into the stream 35407 * 35408 * @param {Function} job the job to push into the stream 35409 */ 35410 }, { 35411 key: 'push', 35412 value: function push(job) { 35413 this.jobs.push(job); 35414 if (!this.timeout_) { 35415 this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay); 35416 } 35417 } 35418 }]); 35419 35420 return AsyncStream; 35421})(_stream2['default']); 35422 35423exports['default'] = AsyncStream; 35424module.exports = exports['default']; 35425},{"./stream":26}],24:[function(require,module,exports){ 35426/** 35427 * @file decrypter.js 35428 * 35429 * An asynchronous implementation of AES-128 CBC decryption with 35430 * PKCS#7 padding. 35431 */ 35432 35433'use strict'; 35434 35435Object.defineProperty(exports, '__esModule', { 35436 value: true 35437}); 35438 35439var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 35440 35441function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 35442 35443function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 35444 35445var _aes = require('./aes'); 35446 35447var _aes2 = _interopRequireDefault(_aes); 35448 35449var _asyncStream = require('./async-stream'); 35450 35451var _asyncStream2 = _interopRequireDefault(_asyncStream); 35452 35453var _pkcs7 = require('pkcs7'); 35454 35455/** 35456 * Convert network-order (big-endian) bytes into their little-endian 35457 * representation. 35458 */ 35459var ntoh = function ntoh(word) { 35460 return word << 24 | (word & 0xff00) << 8 | (word & 0xff0000) >> 8 | word >>> 24; 35461}; 35462 35463/** 35464 * Decrypt bytes using AES-128 with CBC and PKCS#7 padding. 35465 * 35466 * @param {Uint8Array} encrypted the encrypted bytes 35467 * @param {Uint32Array} key the bytes of the decryption key 35468 * @param {Uint32Array} initVector the initialization vector (IV) to 35469 * use for the first round of CBC. 35470 * @return {Uint8Array} the decrypted bytes 35471 * 35472 * @see http://en.wikipedia.org/wiki/Advanced_Encryption_Standard 35473 * @see http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Block_Chaining_.28CBC.29 35474 * @see https://tools.ietf.org/html/rfc2315 35475 */ 35476var decrypt = function decrypt(encrypted, key, initVector) { 35477 // word-level access to the encrypted bytes 35478 var encrypted32 = new Int32Array(encrypted.buffer, encrypted.byteOffset, encrypted.byteLength >> 2); 35479 35480 var decipher = new _aes2['default'](Array.prototype.slice.call(key)); 35481 35482 // byte and word-level access for the decrypted output 35483 var decrypted = new Uint8Array(encrypted.byteLength); 35484 var decrypted32 = new Int32Array(decrypted.buffer); 35485 35486 // temporary variables for working with the IV, encrypted, and 35487 // decrypted data 35488 var init0 = undefined; 35489 var init1 = undefined; 35490 var init2 = undefined; 35491 var init3 = undefined; 35492 var encrypted0 = undefined; 35493 var encrypted1 = undefined; 35494 var encrypted2 = undefined; 35495 var encrypted3 = undefined; 35496 35497 // iteration variable 35498 var wordIx = undefined; 35499 35500 // pull out the words of the IV to ensure we don't modify the 35501 // passed-in reference and easier access 35502 init0 = initVector[0]; 35503 init1 = initVector[1]; 35504 init2 = initVector[2]; 35505 init3 = initVector[3]; 35506 35507 // decrypt four word sequences, applying cipher-block chaining (CBC) 35508 // to each decrypted block 35509 for (wordIx = 0;
35509 wordIx < encrypted32.length; wordIx += 4) { 35510 // convert big-endian (network order) words into little-endian 35511 // (javascript order) 35512 encrypted0 = ntoh(encrypted32[wordIx]); 35513 encrypted1 = ntoh(encrypted32[wordIx + 1]); 35514 encrypted2 = ntoh(encrypted32[wordIx + 2]); 35515 encrypted3 = ntoh(encrypted32[wordIx + 3]); 35516 35517 // decrypt the block 35518 decipher.decrypt(encrypted0, encrypted1, encrypted2, encrypted3, decrypted32, wordIx); 35519 35520 // XOR with the IV, and restore network byte-order to obtain the 35521 // plaintext 35522 decrypted32[wordIx] = ntoh(decrypted32[wordIx] ^ init0); 35523 decrypted32[wordIx + 1] = ntoh(decrypted32[wordIx + 1] ^ init1); 35524 decrypted32[wordIx + 2] = ntoh(decrypted32[wordIx + 2] ^ init2); 35525 decrypted32[wordIx + 3] = ntoh(decrypted32[wordIx + 3] ^ init3); 35526 35527 // setup the IV for the next round 35528 init0 = encrypted0; 35529 init1 = encrypted1; 35530 init2 = encrypted2; 35531 init3 = encrypted3; 35532 } 35533 35534 return decrypted; 35535}; 35536 35537exports.decrypt = decrypt; 35538/** 35539 * The `Decrypter` class that manages decryption of AES 35540 * data through `AsyncStream` objects and the `decrypt` 35541 * function 35542 * 35543 * @param {Uint8Array} encrypted the encrypted bytes 35544 * @param {Uint32Array} key the bytes of the decryption key 35545 * @param {Uint32Array} initVector the initialization vector (IV) to 35546 * @param {Function} done the function to run when done 35547 * @class Decrypter 35548 */ 35549 35550var Decrypter = (function () { 35551 function Decrypter(encrypted, key, initVector, done) { 35552 _classCallCheck(this, Decrypter); 35553 35554 var step = Decrypter.STEP; 35555 var encrypted32 = new Int32Array(encrypted.buffer); 35556 var decrypted = new Uint8Array(encrypted.byteLength); 35557 var i = 0; 35558 35559 this.asyncStream_ = new _asyncStream2['default'](); 35560 35561 // split up the encryption job and do the individual chunks asynchronously 35562 this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted)); 35563 for (i = step; i < encrypted32.length; i += step) { 35564 initVector = new Uint32Array([ntoh(encrypted32[i - 4]), ntoh(encrypted32[i - 3]), ntoh(encrypted32[i - 2]), ntoh(encrypted32[i - 1])]); 35565 this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted)); 35566 } 35567 // invoke the done() callback when everything is finished 35568 this.asyncStream_.push(function () { 35569 // remove pkcs#7 padding from the decrypted bytes 35570 done(null, (0, _pkcs7.unpad)(decrypted)); 35571 }); 35572 } 35573 35574 /** 35575 * a getter for step the maximum number of bytes to process at one time 35576 * 35577 * @return {Number} the value of step 32000 35578 */ 35579 35580 _createClass(Decrypter, [{ 35581 key: 'decryptChunk_', 35582 35583 /** 35584 * @private 35585 */ 35586 value: function decryptChunk_(encrypted, key, initVector, decrypted) { 35587 return function () { 35588 var bytes = decrypt(encrypted, key, initVector); 35589 35590 decrypted.set(bytes, encrypted.byteOffset); 35591 }; 35592 } 35593 }], [{ 35594 key: 'STEP', 35595 get: function get() { 35596 // 4 * 8000; 35597 return 32000; 35598 } 35599 }]); 35600 35601 return Decrypter; 35602})(); 35603 35604exports.Decrypter = Decrypter; 35605exports['default'] = { 35606 Decrypter: Decrypter, 35607 decrypt: decrypt 35608}; 35609},{"./aes":22,"./async-stream":23,"pkcs7":28}],25:[function(require,module,exports){ 35610/** 35611 * @file index.js 35612 * 35613 * Index module to easily import the primary components of AES-128 35614 * decryption. Like this: 35615 * 35616 * ```js 35617 * import {Decrypter, decrypt, AsyncStream} from 'aes-decrypter'; 35618 * ``` 35619 */ 35620'use strict'; 35621 35622Object.defineProperty(exports, '__esModule', { 35623 value: true 35624}); 35625 35626function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 35627 35628var _decrypter = require('./decrypter'); 35629 35630var _asyncStream = require('./async-stream'); 35631 35632var _asyncStream2 = _interopRequireDefault(_asyncStream); 35633 35634exports['default'] = { 35635 decrypt: _decrypter.decrypt, 35636 Decrypter: _decrypter.Decrypter, 35637 AsyncStream: _asyncStream2['default'] 35638}; 35639module.exports = exports['default']; 35640},{"./async-stream":23,"./decrypter":24}],26:[function(require,module,exports){ 35641/** 35642 * @file stream.js 35643 */ 35644/** 35645 * A lightweight readable stream implemention that handles event dispatching. 35646 * 35647 * @class Stream 35648 */ 35649'use strict'; 35650 35651Object.defineProperty(exports, '__esModule', { 35652 value: true 35653}); 35654 35655var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 35656 35657function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 35658 35659var Stream = (function () { 35660 function Stream() { 35661 _classCallCheck(this, Stream); 35662 35663 this.listeners = {}; 35664 } 35665 35666 /** 35667 * Add a listener for a specified event type. 35668 * 35669 * @param {String} type the event name 35670 * @param {Function} listener the callback to be invoked when an event of 35671 * the specified type occurs 35672 */ 35673 35674 _createClass(Stream, [{ 35675 key: 'on', 35676 value: function on(type, listener) { 35677 if (!this.listeners[type]) { 35678 this.listeners[type] = []; 35679 } 35680 this.listeners[type].push(listener); 35681 } 35682 35683 /** 35684 * Remove a listener for a specified event type. 35685 * 35686 * @param {String} type the event name 35687 * @param {Function} listener a function previously registered for this 35688 * type of event through `on` 35689 * @return {Boolean} if we could turn it off or not 35690 */ 35691 }, { 35692 key: 'off', 35693 value: function off(type, listener) { 35694 var index = undefined; 35695 35696 if (!this.listeners[type]) { 35697 return false;
35698 } 35699 index = this.listeners[type].indexOf(listener); 35700 this.listeners[type].splice(index, 1); 35701 return index > -1; 35702 } 35703 35704 /** 35705 * Trigger an event of the specified type on this stream. Any additional 35706 * arguments to this function are passed as parameters to event listeners. 35707 * 35708 * @param {String} type the event name 35709 */ 35710 }, { 35711 key: 'trigger', 35712 value: function trigger(type) { 35713 var callbacks = undefined; 35714 var i = undefined; 35715 var length = undefined; 35716 var args = undefined; 35717 35718 callbacks = this.listeners[type]; 35719 if (!callbacks) { 35720 return; 35721 } 35722 // Slicing the arguments on every invocation of this method 35723 // can add a significant amount of overhead. Avoid the 35724 // intermediate object creation for the common case of a 35725 // single callback argument 35726 if (arguments.length === 2) { 35727 length = callbacks.length; 35728 for (i = 0; i < length; ++i) { 35729 callbacks[i].call(this, arguments[1]); 35730 } 35731 } else { 35732 args = Array.prototype.slice.call(arguments, 1); 35733 length = callbacks.length; 35734 for (i = 0; i < length; ++i) { 35735 callbacks[i].apply(this, args); 35736 } 35737 } 35738 } 35739 35740 /** 35741 * Destroys the stream and cleans up. 35742 */ 35743 }, { 35744 key: 'dispose', 35745 value: function dispose() { 35746 this.listeners = {}; 35747 } 35748 35749 /** 35750 * Forwards all `data` events on this stream to the destination stream. The 35751 * destination stream should provide a method `push` to receive the data 35752 * events as they arrive. 35753 * 35754 * @param {Stream} destination the stream that will receive all `data` events 35755 * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options 35756 */ 35757 }, { 35758 key: 'pipe', 35759 value: function pipe(destination) { 35760 this.on('data', function (data) { 35761 destination.push(data); 35762 }); 35763 } 35764 }]); 35765 35766 return Stream; 35767})(); 35768 35769exports['default'] = Stream; 35770module.exports = exports['default']; 35771},{}],27:[function(require,module,exports){ 35772/* 35773 * pkcs7.pad 35774 * https://github.com/brightcove/pkcs7 35775 * 35776 * Copyright (c) 2014 Brightcove 35777 * Licensed under the apache2 license. 35778 */ 35779 35780'use strict'; 35781 35782var PADDING; 35783 35784/** 35785 * Returns a new Uint8Array that is padded with PKCS#7 padding. 35786 * @param plaintext {Uint8Array} the input bytes before encryption 35787 * @return {Uint8Array} the padded bytes 35788 * @see http://tools.ietf.org/html/rfc5652 35789 */ 35790module.exports = function pad(plaintext) { 35791 var padding = PADDING[(plaintext.byteLength % 16) || 0], 35792 result = new Uint8Array(plaintext.byteLength + padding.length); 35793 result.set(plaintext); 35794 result.set(padding, plaintext.byteLength); 35795 return result; 35796}; 35797 35798// pre-define the padding values 35799PADDING = [ 35800 [16, 16, 16, 16, 35801 16, 16, 16, 16, 35802 16, 16, 16, 16, 35803 16, 16, 16, 16], 35804 35805 [15, 15, 15, 15, 35806 15, 15, 15, 15, 35807 15, 15, 15, 15, 35808 15, 15, 15], 35809 35810 [14, 14, 14, 14, 35811 14, 14, 14, 14, 35812 14, 14, 14, 14, 35813 14, 14], 35814 35815 [13, 13, 13, 13, 35816 13, 13, 13, 13, 35817 13, 13, 13, 13, 35818 13], 35819 35820 [12, 12, 12, 12, 35821 12, 12, 12, 12, 35822 12, 12, 12, 12], 35823 35824 [11, 11, 11, 11, 35825 11, 11, 11, 11, 35826 11, 11, 11], 35827 35828 [10, 10, 10, 10, 35829 10, 10, 10, 10, 35830 10, 10], 35831 35832 [9, 9, 9, 9, 35833 9, 9, 9, 9, 35834 9], 35835 35836 [8, 8, 8, 8, 35837 8, 8, 8, 8], 35838 35839 [7, 7, 7, 7, 35840 7, 7, 7], 35841 35842 [6, 6, 6, 6, 35843 6, 6], 35844 35845 [5, 5, 5, 5, 35846 5], 35847 35848 [4, 4, 4, 4], 35849 35850 [3, 3, 3], 35851 35852 [2, 2], 35853 35854 [1] 35855]; 35856 35857},{}],28:[function(require,module,exports){ 35858/* 35859 * pkcs7 35860 * https://github.com/brightcove/pkcs7 35861 * 35862 * Copyright (c) 2014 Brightcove 35863 * Licensed under the apache2 license. 35864 */ 35865 35866'use strict'; 35867 35868exports.pad = require('./pad.js'); 35869exports.unpad = require('./unpad.js'); 35870 35871},{"./pad.js":27,"./unpad.js":29}],29:[function(require,module,exports){ 35872/* 35873 * pkcs7.unpad 35874 * https://github.com/brightcove/pkcs7 35875 * 35876 * Copyright (c) 2014 Brightcove 35877 * Licensed under the apache2 license. 35878 */ 35879 35880'use strict'; 35881 35882/** 35883 * Returns the subarray of a Uint8Array without PKCS#7 padding. 35884 * @param padded {Uint8Array} unencrypted bytes that have been padded 35885 * @return {Uint8Array} the unpadded bytes 35886 * @see http://tools.ietf.org/html/rfc5652 35887 */ 35888module.exports = function unpad(padded) { 35889 return padded.subarray(0, padded.byteLength - padded[padded.byteLength - 1]); 35890}; 35891 35892},{}],30:[function(require,module,exports){ 35893 35894},{}],31:[function(require,module,exports){ 35895(function (global){ 35896var topLevel = typeof global !== 'undefined' ? global : 35897 typeof window !== 'undefined' ? window : {} 35898var minDoc = require('min-document'); 35899 35900var doccy; 35901 35902if (typeof document !== 'undefined') { 35903 doccy = document; 35904} else { 35905 doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4']; 35906 35907 if (!doccy) { 35908 doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc; 35909 } 35910} 35911 35912module.exports = doccy; 35913 35914}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 35915},{"min-document":30}],32:[function(require,module,exports){ 35916(function (global){ 35917var win; 35918 35919if (typeof window !== "undefined") { 35920 win = window; 35921} else if (typeof global !== "undefined") { 35922 win = global; 35923} else if (typeof self !== "undefined"){ 35924 win = self; 35925} else { 35926 win = {}; 35927} 35928 35929module.exports = win; 35930 35931}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 35932},{}],33:[function(require,module,exports){ 35933'use strict'; 35934 35935var _lineStream = require('./line-stream'); 35936 35937var _lineStream2 = _interopRequireDefault(_lineStream); 35938 35939var _parseStream = require('./parse-stream'); 35940 35941var _parseStream2 = _interopRequireDefault(_parseStream); 35942 35943var _parser = require('./parser'); 35944 35945var _parser2 = _interopRequireDefault(_parser); 35946 35947function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 35948 35949module.exports = { 35950 LineStream: _lineStream2['default'],
35951 ParseStream: _parseStream2['default'], 35952 Parser: _parser2['default'] 35953}; /** 35954 * @file m3u8/index.js 35955 * 35956 * Utilities for parsing M3U8 files. If the entire manifest is available, 35957 * `Parser` will create an object representation with enough detail for managing 35958 * playback. `ParseStream` and `LineStream` are lower-level parsing primitives 35959 * that do not assume the entirety of the manifest is ready and expose a 35960 * ReadableStream-like interface. 35961 */ 35962},{"./line-stream":34,"./parse-stream":35,"./parser":36}],34:[function(require,module,exports){ 35963'use strict'; 35964 35965Object.defineProperty(exports, "__esModule", { 35966 value: true 35967}); 35968 35969var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 35970 35971var _stream = require('./stream'); 35972 35973var _stream2 = _interopRequireDefault(_stream); 35974 35975function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 35976 35977function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 35978 35979function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 35980 35981function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 35982 * @file m3u8/line-stream.js 35983 */ 35984 35985 35986/** 35987 * A stream that buffers string input and generates a `data` event for each 35988 * line. 35989 * 35990 * @class LineStream 35991 * @extends Stream 35992 */ 35993var LineStream = function (_Stream) { 35994 _inherits(LineStream, _Stream); 35995 35996 function LineStream() { 35997 _classCallCheck(this, LineStream); 35998 35999 var _this = _possibleConstructorReturn(this, (LineStream.__proto__ || Object.getPrototypeOf(LineStream)).call(this)); 36000 36001 _this.buffer = ''; 36002 return _this; 36003 } 36004 36005 /** 36006 * Add new data to be parsed. 36007 * 36008 * @param {String} data the text to process 36009 */ 36010 36011 36012 _createClass(LineStream, [{ 36013 key: 'push', 36014 value: function push(data) { 36015 var nextNewline = void 0; 36016 36017 this.buffer += data; 36018 nextNewline = this.buffer.indexOf('\n'); 36019 36020 for (; nextNewline > -1; nextNewline = this.buffer.indexOf('\n')) { 36021 this.trigger('data', this.buffer.substring(0, nextNewline)); 36022 this.buffer = this.buffer.substring(nextNewline + 1); 36023 } 36024 } 36025 }]); 36026 36027 return LineStream; 36028}(_stream2['default']); 36029 36030exports['default'] = LineStream; 36031},{"./stream":37}],35:[function(require,module,exports){ 36032'use strict'; 36033 36034Object.defineProperty(exports, "__esModule", { 36035 value: true 36036}); 36037 36038var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); }
vendor: 60,067 bytes, lines 36038-37890
36038 else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); 36039 36040var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 36041 36042var _stream = require('./stream'); 36043 36044var _stream2 = _interopRequireDefault(_stream); 36045 36046function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 36047 36048function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 36049 36050function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 36051 36052function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 36053 * @file m3u8/parse-stream.js 36054 */ 36055 36056 36057/** 36058 * "forgiving" attribute list psuedo-grammar: 36059 * attributes -> keyvalue (',' keyvalue)* 36060 * keyvalue -> key '=' value 36061 * key -> [^=]* 36062 * value -> '"' [^"]* '"' | [^,]* 36063 */ 36064var attributeSeparator = function attributeSeparator() { 36065 var key = '[^=]*'; 36066 var value = '"[^"]*"|[^,]*'; 36067 var keyvalue = '(?:' + key + ')=(?:' + value + ')'; 36068 36069 return new RegExp('(?:^|,)(' + keyvalue + ')'); 36070}; 36071 36072/** 36073 * Parse attributes from a line given the seperator 36074 * 36075 * @param {String} attributes the attibute line to parse 36076 */ 36077var parseAttributes = function parseAttributes(attributes) { 36078 // split the string using attributes as the separator 36079 var attrs = attributes.split(attributeSeparator()); 36080 var result = {}; 36081 var i = attrs.length; 36082 var attr = void 0; 36083 36084 while (i--) { 36085 // filter out unmatched portions of the string 36086 if (attrs[i] === '') { 36087 continue; 36088 } 36089 36090 // split the key and value 36091 attr = /([^=]*)=(.*)/.exec(attrs[i]).slice(1); 36092 // trim whitespace and remove optional quotes around the value 36093 attr[0] = attr[0].replace(/^\s+|\s+$/g, ''); 36094 attr[1] = attr[1].replace(/^\s+|\s+$/g, ''); 36095 attr[1] = attr[1].replace(/^['"](.*)['"]$/g, '$1'); 36096 result[attr[0]] = attr[1]; 36097 } 36098 return result; 36099}; 36100 36101/** 36102 * A line-level M3U8 parser event stream. It expects to receive input one 36103 * line at a time and performs a context-free parse of its contents. A stream 36104 * interpretation of a manifest can be useful if the manifest is expected to 36105 * be too large to fit comfortably into memory or the entirety of the input 36106 * is not immediately available. Otherwise, it's probably much easier to work 36107 * with a regular `Parser` object. 36108 * 36109 * Produces `data` events with an object that captures the parser's 36110 * interpretation of the input. That object has a property `tag` that is one 36111 * of `uri`, `comment`, or `tag`. URIs only have a single additional 36112 * property, `line`, which captures the entirety of the input without 36113 * interpretation. Comments similarly have a single additional property 36114 * `text` which is the input without the leading `#`. 36115 * 36116 * Tags always have a property `tagType` which is the lower-cased version of 36117 * the M3U8 directive without the `#EXT` or `#EXT-X-` prefix. For instance, 36118 * `#EXT-X-MEDIA-SEQUENCE` becomes `media-sequence` when parsed. Unrecognized 36119 * tags are given the tag type `unknown` and a single additional property 36120 * `data` with the remainder of the input. 36121 * 36122 * @class ParseStream 36123 * @extends Stream 36124 */ 36125 36126var ParseStream = function (_Stream) { 36127 _inherits(ParseStream, _Stream); 36128 36129 function ParseStream() { 36130 _classCallCheck(this, ParseStream); 36131 36132 return _possibleConstructorReturn(this, (ParseStream.__proto__ || Object.getPrototypeOf(ParseStream)).call(this)); 36133 } 36134 36135 /** 36136 * Parses an additional line of input. 36137 * 36138 * @param {String} line a single line of an M3U8 file to parse 36139 */ 36140 36141 36142 _createClass(ParseStream, [{ 36143 key: 'push', 36144 value: function push(line) { 36145 var match = void 0; 36146 var event = void 0; 36147 36148 // strip whitespace 36149 line = line.replace(/^[\u0000\s]+|[\u0000\s]+$/g, ''); 36150 if (line.length === 0) { 36151 // ignore empty lines 36152 return; 36153 } 36154 36155 // URIs 36156 if (line[0] !== '#') { 36157 this.trigger('data', { 36158 type: 'uri', 36159 uri: line 36160 }); 36161 return; 36162 } 36163 36164 // Comments 36165 if (line.indexOf('#EXT') !== 0) { 36166 this.trigger('data', { 36167 type: 'comment', 36168 text: line.slice(1) 36169 }); 36170 return; 36171 } 36172 36173 // strip off any carriage returns here so the regex matching 36174 // doesn't have to account for them. 36175 line = line.replace('\r', ''); 36176 36177 // Tags 36178 match = /^#EXTM3U/.exec(line); 36179 if (match) { 36180 this.trigger('data', { 36181 type: 'tag', 36182 tagType: 'm3u' 36183 }); 36184 return; 36185 } 36186 match = /^#EXTINF:?([0-9\.]*)?,?(.*)?$/.exec(line); 36187 if (match) { 36188 event = { 36189 type: 'tag', 36190 tagType: 'inf' 36191 }; 36192 if (match[1]) { 36193 event.duration = parseFloat(match[1]); 36194 } 36195 if (match[2]) { 36196 event.title = match[2]; 36197 } 36198 this.trigger('data', event); 36199 return; 36200 } 36201 match = /^#EXT-X-TARGETDURATION:?([0-9.]*)?/.exec(line); 36202 if (match) { 36203 event = { 36204 type: 'tag', 36205 tagType: 'targetduration' 36206 }; 36207 if (match[1]) { 36208 event.duration = parseInt(match[1], 10); 36209 } 36210 this.trigger('data', event); 36211 return; 36212 } 36213 match = /^#ZEN-TOTAL-DURATION:?([0-9.]*)?/.exec(line); 36214 if (match) { 36215 event = { 36216 type: 'tag', 36217 tagType: 'totalduration' 36218 }; 36219 if (match[1]) { 36220 event.duration = parseInt(match[1], 10); 36221 } 36222 this.trigger('data', event); 36223 return; 36224 } 36225 match = /^#EXT-X-VERSION:?([0-9.]*)?/.exec(line); 36226 if (match) { 36227 event = { 36228 type: 'tag', 36229 tagType: 'version' 36230 }; 36231 if (match[1]) { 36232 event.version = parseInt(match[1], 10); 36233 } 36234 this.trigger('data', event); 36235 return; 36236 } 36237 match = /^#EXT-X-MEDIA-SEQUENCE:?(\-?[0-9.]*)?/.exec(line); 36238 if (match) { 36239 event = { 36240 type: 'tag', 36241 tagType: 'media-sequence' 36242 }; 36243 if (match[1]) { 36244 event.number = parseInt(match[1], 10); 36245 } 36246 this.trigger('data', event); 36247 return; 36248 } 36249 match = /^#EXT-X-DISCONTINUITY-SEQUENCE:?(\-?[0-9.]*)?/.exec(line); 36250 if (match) { 36251 event = { 36252 type: 'tag', 36253 tagType: 'discontinuity-sequence' 36254 }; 36255 if (match[1]) { 36256 event.number = parseInt(match[1], 10); 36257 } 36258 this.trigger('data', event); 36259 return; 36260 } 36261 match = /^#EXT-X-PLAYLIST-TYPE:?(.*)?$/.exec(line); 36262 if (match) { 36263 event = { 36264 type: 'tag', 36265 tagType: 'playlist-type' 36266 }; 36267 if (match[1]) { 36268 event.playlistType = match[1]; 36269 } 36270 this.trigger('data', event); 36271 return; 36272 } 36273 match = /^#EXT-X-BYTERANGE:?([0-9.]*)?@?([0-9.]*)?/.exec(line); 36274 if (match) { 36275 event = { 36276 type: 'tag', 36277 tagType: 'byterange' 36278 }; 36279 if (match[1]) { 36280 event.length = parseInt(match[1], 10); 36281 } 36282 if (match[2]) { 36283 event.offset = parseInt(match[2], 10); 36284 } 36285 this.trigger('data', event); 36286 return; 36287 } 36288 match = /^#EXT-X-ALLOW-CACHE:?(YES|NO)?/.exec(line); 36289 if (match) { 36290 event = { 36291 type: 'tag', 36292 tagType: 'allow-cache' 36293 }; 36294 if (match[1]) { 36295 event.allowed = !/NO/.test(match[1]); 36296 } 36297 this.trigger('data', event); 36298 return; 36299 } 36300 match = /^#EXT-X-MAP:?(.*)$/.exec(line); 36301 if (match) { 36302 event = { 36303 type: 'tag', 36304 tagType: 'map' 36305 }; 36306 36307 if (match[1]) { 36308 var attributes = parseAttributes(match[1]); 36309 36310 if (attributes.URI) { 36311 event.uri = attributes.URI; 36312 } 36313 if (attributes.BYTERANGE) { 36314 var _attributes$BYTERANGE = attributes.BYTERANGE.split('@'), 36315 _attributes$BYTERANGE2 = _slicedToArray(_attributes$BYTERANGE, 2), 36316 length = _attributes$BYTERANGE2[0], 36317 offset = _attributes$BYTERANGE2[1]; 36318 36319 event.byterange = {}; 36320 if (length) { 36321 event.byterange.length = parseInt(length, 10); 36322 } 36323 if (offset) { 36324 event.byterange.offset = parseInt(offset, 10); 36325 } 36326 } 36327 } 36328 36329 this.trigger('data', event); 36330 return; 36331 } 36332 match = /^#EXT-X-STREAM-INF:?(.*)$/.exec(line); 36333 if (match) { 36334 event = { 36335 type: 'tag', 36336 tagType: 'stream-inf' 36337 }; 36338 if (match[1]) { 36339 event.attributes = parseAttributes(match[1]); 36340 36341 if (event.attributes.RESOLUTION) { 36342 var split = event.attributes.RESOLUTION.split('x'); 36343 var resolution = {}; 36344 36345 if (split[0]) { 36346 resolution.width = parseInt(split[0], 10); 36347 } 36348 if (split[1]) { 36349 resolution.height = parseInt(split[1], 10); 36350 } 36351 event.attributes.RESOLUTION = resolution; 36352 } 36353 if (event.attributes.BANDWIDTH) { 36354 event.attributes.BANDWIDTH = parseInt(event.attributes.BANDWIDTH, 10); 36355 } 36356 if (event.attributes['PROGRAM-ID']) { 36357 event.attributes['PROGRAM-ID'] = parseInt(event.attributes['PROGRAM-ID'], 10); 36358 } 36359 } 36360 this.trigger('data', event); 36361 return; 36362 } 36363 match = /^#EXT-X-MEDIA:?(.*)$/.exec(line); 36364 if (match) { 36365 event = { 36366 type: 'tag', 36367 tagType: 'media' 36368 }; 36369 if (match[1]) { 36370 event.attributes = parseAttributes(match[1]); 36371 } 36372 this.trigger('data', event); 36373 return; 36374 } 36375 match = /^#EXT-X-ENDLIST/.exec(line); 36376 if (match) { 36377 this.trigger('data', { 36378 type: 'tag', 36379 tagType: 'endlist' 36380 }); 36381 return; 36382 } 36383 match = /^#EXT-X-DISCONTINUITY/.exec(line); 36384 if (match) { 36385 this.trigger('data', { 36386 type: 'tag', 36387 tagType: 'discontinuity' 36388 }); 36389 return; 36390 } 36391 match = /^#EXT-X-PROGRAM-DATE-TIME:?(.*)$/.exec(line); 36392 if (match) { 36393 event = { 36394 type: 'tag', 36395 tagType: 'program-date-time' 36396 }; 36397 if (match[1]) { 36398 event.dateTimeString = match[1]; 36399 event.dateTimeObject = new Date(match[1]); 36400 } 36401 this.trigger('data', event); 36402 return; 36403 } 36404 match = /^#EXT-X-KEY:?(.*)$/.exec(line); 36405 if (match) { 36406 event = { 36407 type: 'tag', 36408 tagType: 'key' 36409 }; 36410 if (match[1]) { 36411 event.attributes = parseAttributes(match[1]); 36412 // parse the IV string into a Uint32Array 36413 if (event.attributes.IV) { 36414 if (event.attributes.IV.substring(0, 2).toLowerCase() === '0x') { 36415 event.attributes.IV = event.attributes.IV.substring(2); 36416 } 36417 36418 event.attributes.IV = event.attributes.IV.match(/.{8}/g); 36419 event.attributes.IV[0] = parseInt(event.attributes.IV[0], 16); 36420 event.attributes.IV[1] = parseInt(event.attributes.IV[1], 16); 36421 event.attributes.IV[2] = parseInt(event.attributes.IV[2], 16); 36422 event.attributes.IV[3] = parseInt(event.attributes.IV[3], 16); 36423 event.attributes.IV = new Uint32Array(event.attributes.IV); 36424 } 36425 } 36426 this.trigger('data', event); 36427 return; 36428 } 36429 match = /^#EXT-X-CUE-OUT-CONT:?(.*)?$/.exec(line); 36430 if (match) { 36431 event = { 36432 type: 'tag', 36433 tagType: 'cue-out-cont' 36434 }; 36435 if (match[1]) { 36436 event.data = match[1]; 36437 } else { 36438 event.data = ''; 36439 } 36440 this.trigger('data', event); 36441 return; 36442 } 36443 match = /^#EXT-X-CUE-OUT:?(.*)?$/.exec(line); 36444 if (match) { 36445 event = { 36446 type: 'tag', 36447 tagType: 'cue-out' 36448 }; 36449 if (match[1]) { 36450 event.data = match[1]; 36451 } else { 36452 event.data = ''; 36453 } 36454 this.trigger('data', event); 36455 return; 36456 } 36457 match = /^#EXT-X-CUE-IN:?(.*)?$/.exec(line); 36458 if (match) { 36459 event = { 36460 type: 'tag', 36461 tagType: 'cue-in' 36462 }; 36463 if (match[1]) { 36464 event.data = match[1]; 36465 } else { 36466 event.data = ''; 36467 } 36468 this.trigger('data', event); 36469 return; 36470 } 36471 36472 // unknown tag type 36473 this.trigger('data', { 36474 type: 'tag', 36475 data: line.slice(4) 36476 }); 36477 } 36478 }]); 36479 36480 return ParseStream; 36481}(_stream2['default']); 36482 36483exports['default'] = ParseStream; 36484},{"./stream":37}],36:[function(require,module,exports){ 36485'use strict'; 36486 36487Object.defineProperty(exports, "__esModule", { 36488 value: true 36489}); 36490 36491var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; 36492 36493var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 36494 36495var _stream = require('./stream'); 36496 36497var _stream2 = _interopRequireDefault(_stream); 36498 36499var _lineStream = require('./line-stream'); 36500 36501var _lineStream2 = _interopRequireDefault(_lineStream); 36502 36503var _parseStream = require('./parse-stream'); 36504 36505var _parseStream2 = _interopRequireDefault(_parseStream); 36506 36507function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 36508 36509function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 36510 36511function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 36512 36513function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** 36514 * @file m3u8/parser.js 36515 */ 36516 36517 36518/** 36519 * A parser for M3U8 files. The current interpretation of the input is 36520 * exposed as a property `manifest` on parser objects. It's just two lines to 36521 * create and parse a manifest once you have the contents available as a string: 36522 * 36523 * ```js 36524 * var parser = new m3u8.Parser(); 36525 * parser.push(xhr.responseText); 36526 * ``` 36527 * 36528 * New input can later be applied to update the manifest object by calling 36529 * `push` again. 36530 * 36531 * The parser attempts to create a usable manifest object even if the 36532 * underlying input is somewhat nonsensical. It emits `info` and `warning` 36533 * events during the parse if it encounters input that seems invalid or 36534 * requires some property of the manifest object to be defaulted. 36535 * 36536 * @class Parser 36537 * @extends Stream 36538 */ 36539var Parser = function (_Stream) { 36540 _inherits(Parser, _Stream); 36541 36542 function Parser() { 36543 _classCallCheck(this, Parser); 36544 36545 var _this = _possibleConstructorReturn(this, (Parser.__proto__ || Object.getPrototypeOf(Parser)).call(this)); 36546 36547 _this.lineStream = new _lineStream2['default'](); 36548 _this.parseStream = new _parseStream2['default'](); 36549 _this.lineStream.pipe(_this.parseStream); 36550 /* eslint-disable consistent-this */ 36551 var self = _this; 36552 /* eslint-enable consistent-this */ 36553 var uris = []; 36554 var currentUri = {}; 36555 // if specified, the active EXT-X-MAP definition 36556 var currentMap = void 0; 36557 // if specified, the active decryption key 36558 var _key = void 0; 36559 var noop = function noop() {}; 36560 var defaultMediaGroups = { 36561 'AUDIO': {}, 36562 'VIDEO': {}, 36563 'CLOSED-CAPTIONS': {}, 36564 'SUBTITLES': {} 36565 }; 36566 // group segments into numbered timelines delineated by discontinuities 36567 var currentTimeline = 0; 36568 36569 // the manifest is empty until the parse stream begins delivering data 36570 _this.manifest = { 36571 allowCache: true, 36572 discontinuityStarts: [], 36573 segments: [] 36574 }; 36575 36576 // update the manifest with the m3u8 entry from the parse stream 36577 _this.parseStream.on('data', function (entry) { 36578 var mediaGroup = void 0; 36579 var rendition = void 0; 36580 36581 ({ 36582 tag: function tag() { 36583 // switch based on the tag type 36584 (({ 36585 'allow-cache': function allowCache() { 36586 this.manifest.allowCache = entry.allowed; 36587 if (!('allowed' in entry)) { 36588 this.trigger('info', { 36589 message: 'defaulting allowCache to YES' 36590 }); 36591 this.manifest.allowCache = true; 36592 } 36593 }, 36594 byterange: function byterange() { 36595 var byterange = {}; 36596 36597 if ('length' in entry) { 36598 currentUri.byterange = byterange; 36599 byterange.length = entry.length; 36600 36601 if (!('offset' in entry)) { 36602 this.trigger('info', { 36603 message: 'defaulting offset to zero' 36604 }); 36605 entry.offset = 0; 36606 } 36607 } 36608 if ('offset' in entry) { 36609 currentUri.byterange = byterange; 36610 byterange.offset = entry.offset; 36611 } 36612 }, 36613 endlist: function endlist() { 36614 this.manifest.endList = true; 36615 }, 36616 inf: function inf() { 36617 if (!('mediaSequence' in this.manifest)) { 36618 this.manifest.mediaSequence = 0; 36619 this.trigger('info', { 36620 message: 'defaulting media sequence to zero' 36621 }); 36622 } 36623 if (!('discontinuitySequence' in this.manifest)) { 36624 this.manifest.discontinuitySequence = 0; 36625 this.trigger('info', { 36626 message: 'defaulting discontinuity sequence to zero' 36627 }); 36628 } 36629 if (entry.duration > 0) { 36630 currentUri.duration = entry.duration; 36631 } 36632 36633 if (entry.duration === 0) { 36634 currentUri.duration = 0.01; 36635 this.trigger('info', { 36636 message: 'updating zero segment duration to a small value' 36637 }); 36638 } 36639 36640 this.manifest.segments = uris; 36641 }, 36642 key: function key() { 36643 if (!entry.attributes) { 36644 this.trigger('warn', { 36645 message: 'ignoring key declaration without attribute list' 36646 }); 36647 return; 36648 } 36649 // clear the active encryption key 36650 if (entry.attributes.METHOD === 'NONE') { 36651 _key = null; 36652 return; 36653 } 36654 if (!entry.attributes.URI) { 36655 this.trigger('warn', { 36656 message: 'ignoring key declaration without URI' 36657 }); 36658 return; 36659 } 36660 if (!entry.attributes.METHOD) { 36661 this.trigger('warn', { 36662 message: 'defaulting key method to AES-128' 36663 }); 36664 } 36665 36666 // setup an encryption key for upcoming segments 36667 _key = { 36668 method: entry.attributes.METHOD || 'AES-128', 36669 uri: entry.attributes.URI 36670 }; 36671 36672 if (typeof entry.attributes.IV !== 'undefined') { 36673 _key.iv = entry.attributes.IV; 36674 } 36675 }, 36676 'media-sequence': function mediaSequence() { 36677 if (!isFinite(entry.number)) { 36678 this.trigger('warn', { 36679 message: 'ignoring invalid media sequence: ' + entry.number 36680 }); 36681 return; 36682 } 36683 this.manifest.mediaSequence = entry.number; 36684 }, 36685 'discontinuity-sequence': function discontinuitySequence() { 36686 if (!isFinite(entry.number)) { 36687 this.trigger('warn', { 36688 message: 'ignoring invalid discontinuity sequence: ' + entry.number 36689 }); 36690 return; 36691 } 36692 this.manifest.discontinuitySequence = entry.number; 36693 currentTimeline = entry.number; 36694 }, 36695 'playlist-type': function playlistType() { 36696 if (!/VOD|EVENT/.test(entry.playlistType)) { 36697 this.trigger('warn', { 36698 message: 'ignoring unknown playlist type: ' + entry.playlist 36699 }); 36700 return; 36701 } 36702 this.manifest.playlistType = entry.playlistType; 36703 }, 36704 map: function map() { 36705 currentMap = {}; 36706 if (entry.uri) { 36707 currentMap.uri = entry.uri; 36708 } 36709 if (entry.byterange) { 36710 currentMap.byterange = entry.byterange; 36711 } 36712 }, 36713 'stream-inf': function streamInf() { 36714 this.manifest.playlists = uris; 36715 this.manifest.mediaGroups = this.manifest.mediaGroups || defaultMediaGroups; 36716 36717 if (!entry.attributes) { 36718 this.trigger('warn', { 36719 message: 'ignoring empty stream-inf attributes' 36720 }); 36721 return; 36722 } 36723 36724 if (!currentUri.attributes) { 36725 currentUri.attributes = {}; 36726 } 36727 _extends(currentUri.attributes, entry.attributes); 36728 }, 36729 media: function media() { 36730 this.manifest.mediaGroups = this.manifest.mediaGroups || defaultMediaGroups; 36731 36732 if (!(entry.attributes && entry.attributes.TYPE && entry.attributes['GROUP-ID'] && entry.attributes.NAME)) { 36733 this.trigger('warn', { 36734 message: 'ignoring incomplete or missing media group' 36735 }); 36736 return; 36737 } 36738 36739 // find the media group, creating defaults as necessary 36740 var mediaGroupType = this.manifest.mediaGroups[entry.attributes.TYPE]; 36741 36742 mediaGroupType[entry.attributes['GROUP-ID']] = mediaGroupType[entry.attributes['GROUP-ID']] || {}; 36743 mediaGroup = mediaGroupType[entry.attributes['GROUP-ID']]; 36744 36745 // collect the rendition metadata 36746 rendition = { 36747 'default': /yes/i.test(entry.attributes.DEFAULT) 36748 }; 36749 if (rendition['default']) { 36750 rendition.autoselect = true; 36751 } else { 36752 rendition.autoselect = /yes/i.test(entry.attributes.AUTOSELECT); 36753 } 36754 if (entry.attributes.LANGUAGE) { 36755 rendition.language = entry.attributes.LANGUAGE; 36756 } 36757 if (entry.attributes.URI) { 36758 rendition.uri = entry.attributes.URI; 36759 } 36760 if (entry.attributes['INSTREAM-ID']) { 36761 rendition.instreamId = entry.attributes['INSTREAM-ID']; 36762 } 36763 if (entry.attributes.CHARACTERISTICS) { 36764 rendition.characteristics = entry.attributes.CHARACTERISTICS; 36765 } 36766 if (entry.attributes.FORCED) { 36767 rendition.forced = /yes/i.test(entry.attributes.FORCED); 36768 } 36769 36770 // insert the new rendition 36771 mediaGroup[entry.attributes.NAME] = rendition; 36772 }, 36773 discontinuity: function discontinuity() { 36774 currentTimeline += 1; 36775 currentUri.discontinuity = true; 36776 this.manifest.discontinuityStarts.push(uris.length); 36777 }, 36778 'program-date-time': function programDateTime() { 36779 this.manifest.dateTimeString = entry.dateTimeString; 36780 this.manifest.dateTimeObject = entry.dateTimeObject; 36781 }, 36782 targetduration: function targetduration() { 36783 if (!isFinite(entry.duration) || entry.duration < 0) { 36784 this.trigger('warn', { 36785 message: 'ignoring invalid target duration: ' + entry.duration 36786 }); 36787 return; 36788 } 36789 this.manifest.targetDuration = entry.duration; 36790 }, 36791 totalduration: function totalduration() { 36792 if (!isFinite(entry.duration) || entry.duration < 0) { 36793 this.trigger('warn', { 36794 message: 'ignoring invalid total duration: ' + entry.duration 36795 }); 36796 return; 36797 } 36798 this.manifest.totalDuration = entry.duration; 36799 }, 36800 'cue-out': function cueOut() { 36801 currentUri.cueOut = entry.data; 36802 }, 36803 'cue-out-cont': function cueOutCont() { 36804 currentUri.cueOutCont = entry.data; 36805 }, 36806 'cue-in': function cueIn() { 36807 currentUri.cueIn = entry.data; 36808 } 36809 })[entry.tagType] || noop).call(self); 36810 }, 36811 uri: function uri() { 36812 currentUri.uri = entry.uri; 36813 uris.push(currentUri); 36814 36815 // if no explicit duration was declared, use the target duration 36816 if (this.manifest.targetDuration && !('duration' in currentUri)) { 36817 this.trigger('warn', { 36818 message: 'defaulting segment duration to the target duration' 36819 }); 36820 currentUri.duration = this.manifest.targetDuration; 36821 } 36822 // annotate with encryption information, if necessary 36823 if (_key) { 36824 currentUri.key = _key; 36825 } 36826 currentUri.timeline = currentTimeline; 36827 // annotate with initialization segment information, if necessary 36828 if (currentMap) { 36829 currentUri.map = currentMap; 36830 } 36831 36832 // prepare for the next URI 36833 currentUri = {}; 36834 }, 36835 comment: function comment() { 36836 // comments are not important for playback 36837 } 36838 })[entry.type].call(self); 36839 }); 36840 36841 return _this; 36842 } 36843 36844 /** 36845 * Parse the input string and update the manifest object. 36846 * 36847 * @param {String} chunk a potentially incomplete portion of the manifest 36848 */ 36849 36850 36851 _createClass(Parser, [{ 36852 key: 'push', 36853 value: function push(chunk) { 36854 this.lineStream.push(chunk); 36855 } 36856 36857 /** 36858 * Flush any remaining input. This can be handy if the last line of an M3U8 36859 * manifest did not contain a trailing newline but the file has been 36860 * completely received. 36861 */ 36862 36863 }, { 36864 key: 'end', 36865 value: function end() { 36866 // flush any buffered input 36867 this.lineStream.push('\n'); 36868 } 36869 }]); 36870 36871 return Parser; 36872}(_stream2['default']); 36873 36874exports['default'] = Parser; 36875},{"./line-stream":34,"./parse-stream":35,"./stream":37}],37:[function(require,module,exports){ 36876'use strict'; 36877 36878Object.defineProperty(exports, "__esModule", { 36879 value: true 36880}); 36881 36882var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 36883 36884function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 36885 36886/** 36887 * @file stream.js 36888 */ 36889/** 36890 * A lightweight readable stream implemention that handles event dispatching. 36891 * 36892 * @class Stream 36893 */ 36894var Stream = function () { 36895 function Stream() { 36896 _classCallCheck(this, Stream); 36897 36898 this.listeners = {}; 36899 } 36900 36901 /** 36902 * Add a listener for a specified event type. 36903 * 36904 * @param {String} type the event name 36905 * @param {Function} listener the callback to be invoked when an event of 36906 * the specified type occurs 36907 */ 36908 36909 36910 _createClass(Stream, [{ 36911 key: 'on', 36912 value: function on(type, listener) { 36913 if (!this.listeners[type]) { 36914 this.listeners[type] = []; 36915 } 36916 this.listeners[type].push(listener); 36917 } 36918 36919 /** 36920 * Remove a listener for a specified event type. 36921 * 36922 * @param {String} type the event name 36923 * @param {Function} listener a function previously registered for this 36924 * type of event through `on` 36925 * @return {Boolean} if we could turn it off or not 36926 */ 36927 36928 }, { 36929 key: 'off', 36930 value: function off(type, listener) { 36931 if (!this.listeners[type]) { 36932 return false; 36933 } 36934 36935 var index = this.listeners[type].indexOf(listener); 36936 36937 this.listeners[type].splice(index, 1); 36938 return index > -1; 36939 } 36940 36941 /** 36942 * Trigger an event of the specified type on this stream. Any additional 36943 * arguments to this function are passed as parameters to event listeners. 36944 * 36945 * @param {String} type the event name 36946 */ 36947 36948 }, { 36949 key: 'trigger', 36950 value: function trigger(type) { 36951 var callbacks = this.listeners[type]; 36952 var i = void 0; 36953 var length = void 0; 36954 var args = void 0; 36955 36956 if (!callbacks) { 36957 return; 36958 } 36959 // Slicing the arguments on every invocation of this method 36960 // can add a significant amount of overhead. Avoid the 36961 // intermediate object creation for the common case of a 36962 // single callback argument 36963 if (arguments.length === 2) { 36964 length = callbacks.length; 36965 for (i = 0; i < length; ++i) { 36966 callbacks[i].call(this, arguments[1]); 36967 } 36968 } else { 36969 args = Array.prototype.slice.call(arguments, 1); 36970 length = callbacks.length; 36971 for (i = 0; i < length; ++i) { 36972 callbacks[i].apply(this, args); 36973 } 36974 } 36975 } 36976 36977 /** 36978 * Destroys the stream and cleans up. 36979 */ 36980 36981 }, { 36982 key: 'dispose', 36983 value: function dispose() { 36984 this.listeners = {}; 36985 } 36986 /** 36987 * Forwards all `data` events on this stream to the destination stream. The 36988 * destination stream should provide a method `push` to receive the data 36989 * events as they arrive. 36990 * 36991 * @param {Stream} destination the stream that will receive all `data` events 36992 * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options 36993 */ 36994 36995 }, { 36996 key: 'pipe', 36997 value: function pipe(destination) { 36998 this.on('data', function (data) { 36999 destination.push(data); 37000 }); 37001 } 37002 }]); 37003 37004 return Stream; 37005}(); 37006 37007exports['default'] = Stream; 37008},{}],38:[function(require,module,exports){ 37009/** 37010 * mux.js 37011 * 37012 * Copyright (c) 2016 Brightcove 37013 * All rights reserved. 37014 * 37015 * A stream-based aac to mp4 converter. This utility can be used to 37016 * deliver mp4s to a SourceBuffer on platforms that support native 37017 * Media Source Extensions. 37018 */ 37019'use strict'; 37020var Stream = require('../utils/stream.js'); 37021 37022// Constants 37023var AacStream; 37024 37025/** 37026 * Splits an incoming stream of binary data into ADTS and ID3 Frames. 37027 */ 37028 37029AacStream = function() { 37030 var 37031 everything = new Uint8Array(), 37032 timeStamp = 0; 37033 37034 AacStream.prototype.init.call(this); 37035 37036 this.setTimestamp = function(timestamp) { 37037 timeStamp = timestamp; 37038 }; 37039 37040 this.parseId3TagSize = function(header, byteIndex) { 37041 var 37042 returnSize = (header[byteIndex + 6] << 21) | 37043 (header[byteIndex + 7] << 14) | 37044 (header[byteIndex + 8] << 7) | 37045 (header[byteIndex + 9]), 37046 flags = header[byteIndex + 5], 37047 footerPresent = (flags & 16) >> 4; 37048 37049 if (footerPresent) { 37050 return returnSize + 20; 37051 } 37052 return returnSize + 10; 37053 }; 37054 37055 this.parseAdtsSize = function(header, byteIndex) { 37056 var 37057 lowThree = (header[byteIndex + 5] & 0xE0) >> 5, 37058 middle = header[byteIndex + 4] << 3, 37059 highTwo = header[byteIndex + 3] & 0x3 << 11; 37060 37061 return (highTwo | middle) | lowThree; 37062 }; 37063 37064 this.push = function(bytes) { 37065 var 37066 frameSize = 0, 37067 byteIndex = 0, 37068 bytesLeft, 37069 chunk, 37070 packet, 37071 tempLength; 37072 37073 // If there are bytes remaining from the last segment, prepend them to the 37074 // bytes that were pushed in 37075 if (everything.length) { 37076 tempLength = everything.length; 37077 everything = new Uint8Array(bytes.byteLength + tempLength); 37078 everything.set(everything.subarray(0, tempLength)); 37079 everything.set(bytes, tempLength); 37080 } else { 37081 everything = bytes; 37082 } 37083 37084 while (everything.length - byteIndex >= 3) { 37085 if ((everything[byteIndex] === 'I'.charCodeAt(0)) && 37086 (everything[byteIndex + 1] === 'D'.charCodeAt(0)) && 37087 (everything[byteIndex + 2] === '3'.charCodeAt(0))) { 37088 37089 // Exit early because we don't have enough to parse 37090 // the ID3 tag header 37091 if (everything.length - byteIndex < 10) { 37092 break; 37093 } 37094 37095 // check framesize 37096 frameSize = this.parseId3TagSize(everything, byteIndex); 37097 37098 // Exit early if we don't have enough in the buffer 37099 // to emit a full packet 37100 if (frameSize > everything.length) { 37101 break; 37102 } 37103 chunk = { 37104 type: 'timed-metadata', 37105 data: everything.subarray(byteIndex, byteIndex + frameSize) 37106 }; 37107 this.trigger('data', chunk); 37108 byteIndex += frameSize; 37109 continue; 37110 } else if ((everything[byteIndex] & 0xff === 0xff) && 37111 ((everything[byteIndex + 1] & 0xf0) === 0xf0)) { 37112 37113 // Exit early because we don't have enough to parse 37114 // the ADTS frame header 37115 if (everything.length - byteIndex < 7) { 37116 break; 37117 } 37118 37119 frameSize = this.parseAdtsSize(everything, byteIndex); 37120 37121 // Exit early if we don't have enough in the buffer 37122 // to emit a full packet 37123 if (frameSize > everything.length) { 37124 break; 37125 } 37126 37127 packet = { 37128 type: 'audio', 37129 data: everything.subarray(byteIndex, byteIndex + frameSize), 37130 pts: timeStamp, 37131 dts: timeStamp 37132 }; 37133 this.trigger('data', packet); 37134 byteIndex += frameSize; 37135 continue; 37136 } 37137 byteIndex++; 37138 } 37139 bytesLeft = everything.length - byteIndex; 37140 37141 if (bytesLeft > 0) { 37142 everything = everything.subarray(byteIndex); 37143 } else { 37144 everything = new Uint8Array(); 37145 } 37146 }; 37147}; 37148 37149AacStream.prototype = new Stream(); 37150 37151module.exports = AacStream; 37152 37153},{"../utils/stream.js":62}],39:[function(require,module,exports){ 37154/** 37155 * mux.js 37156 * 37157 * Copyright (c) 2016 Brightcove 37158 * All rights reserved. 37159 * 37160 * Utilities to detect basic properties and metadata about Aac data. 37161 */ 37162'use strict'; 37163 37164var ADTS_SAMPLING_FREQUENCIES = [ 37165 96000, 37166 88200, 37167 64000, 37168 48000, 37169 44100, 37170 32000, 37171 24000, 37172 22050, 37173 16000, 37174 12000, 37175 11025, 37176 8000, 37177 7350 37178]; 37179 37180var parseSyncSafeInteger = function(data) { 37181 return (data[0] << 21) | 37182 (data[1] << 14) | 37183 (data[2] << 7) | 37184 (data[3]); 37185}; 37186 37187// return a percent-encoded representation of the specified byte range 37188// @see http://en.wikipedia.org/wiki/Percent-encoding 37189var percentEncode = function(bytes, start, end) { 37190 var i, result = ''; 37191 for (i = start; i < end; i++) { 37192 result += '%' + ('00' + bytes[i].toString(16)).slice(-2); 37193 } 37194 return result; 37195}; 37196 37197// return the string representation of the specified byte range, 37198// interpreted as ISO-8859-1. 37199var parseIso88591 = function(bytes, start, end) { 37200 return unescape(percentEncode(bytes, start, end)); // jshint ignore:line 37201}; 37202 37203var parseId3TagSize = function(header, byteIndex) { 37204 var 37205 returnSize = (header[byteIndex + 6] << 21) | 37206 (header[byteIndex + 7] << 14) | 37207 (header[byteIndex + 8] << 7) | 37208 (header[byteIndex + 9]), 37209 flags = header[byteIndex + 5], 37210 footerPresent = (flags & 16) >> 4; 37211 37212 if (footerPresent) { 37213 return returnSize + 20; 37214 } 37215 return returnSize + 10; 37216}; 37217 37218var parseAdtsSize = function(header, byteIndex) { 37219 var 37220 lowThree = (header[byteIndex + 5] & 0xE0) >> 5, 37221 middle = header[byteIndex + 4] << 3, 37222 highTwo = header[byteIndex + 3] & 0x3 << 11; 37223 37224 return (highTwo | middle) | lowThree; 37225}; 37226 37227var parseType = function(header, byteIndex) { 37228 if ((header[byteIndex] === 'I'.charCodeAt(0)) && 37229 (header[byteIndex + 1] === 'D'.charCodeAt(0)) && 37230 (header[byteIndex + 2] === '3'.charCodeAt(0))) { 37231 return 'timed-metadata'; 37232 } else if ((header[byteIndex] & 0xff === 0xff) && 37233 ((header[byteIndex + 1] & 0xf0) === 0xf0)) { 37234 return 'audio'; 37235 } 37236 return null; 37237}; 37238 37239var parseSampleRate = function(packet) { 37240 var i = 0; 37241 37242 while (i + 5 < packet.length) { 37243 if (packet[i] !== 0xFF || (packet[i + 1] & 0xF6) !== 0xF0) { 37244 // If a valid header was not found, jump one forward and attempt to 37245 // find a valid ADTS header starting at the next byte 37246 i++; 37247 continue; 37248 } 37249 return ADTS_SAMPLING_FREQUENCIES[(packet[i + 2] & 0x3c) >>> 2]; 37250 } 37251 37252 return null; 37253}; 37254 37255var parseAacTimestamp = function(packet) { 37256 var frameStart, frameSize, frame, frameHeader; 37257 37258 // find the start of the first frame and the end of the tag 37259 frameStart = 10; 37260 if (packet[5] & 0x40) { 37261 // advance the frame start past the extended header 37262 frameStart += 4; // header size field 37263 frameStart += parseSyncSafeInteger(packet.subarray(10, 14)); 37264 } 37265 37266 // parse one or more ID3 frames 37267 // http://id3.org/id3v2.3.0#ID3v2_frame_overview 37268 do { 37269 // determine the number of bytes in this frame 37270 frameSize = parseSyncSafeInteger(packet.subarray(frameStart + 4, frameStart + 8)); 37271 if (frameSize < 1) { 37272 return null; 37273 } 37274 frameHeader = String.fromCharCode(packet[frameStart], 37275 packet[frameStart + 1], 37276 packet[frameStart + 2], 37277 packet[frameStart + 3]); 37278 37279 if (frameHeader === 'PRIV') { 37280 frame = packet.subarray(frameStart + 10, frameStart + frameSize + 10); 37281 37282 for (var i = 0; i < frame.byteLength; i++) { 37283 if (frame[i] === 0) { 37284 var owner = parseIso88591(frame, 0, i); 37285 if (owner === 'com.apple.streaming.transportStreamTimestamp') { 37286 var d = frame.subarray(i + 1); 37287 var size = ((d[3] & 0x01) << 30) | 37288 (d[4] << 22) | 37289 (d[5] << 14) | 37290 (d[6] << 6) | 37291 (d[7] >>> 2); 37292 size *= 4; 37293 size += d[7] & 0x03; 37294 37295 return size; 37296 } 37297 break; 37298 } 37299 } 37300 } 37301 37302 frameStart += 10; // advance past the frame header 37303 frameStart += frameSize; // advance past the frame body 37304 } while (frameStart < packet.byteLength); 37305 return null; 37306}; 37307 37308module.exports = { 37309 parseId3TagSize: parseId3TagSize, 37310 parseAdtsSize: parseAdtsSize, 37311 parseType: parseType, 37312 parseSampleRate: parseSampleRate, 37313 parseAacTimestamp: parseAacTimestamp 37314}; 37315 37316},{}],40:[function(require,module,exports){ 37317'use strict'; 37318 37319var Stream = require('../utils/stream.js'); 37320 37321var AdtsStream; 37322 37323var 37324 ADTS_SAMPLING_FREQUENCIES = [ 37325 96000, 37326 88200, 37327 64000, 37328 48000, 37329 44100, 37330 32000, 37331 24000, 37332 22050, 37333 16000, 37334 12000, 37335 11025, 37336 8000, 37337 7350 37338 ]; 37339 37340/* 37341 * Accepts a ElementaryStream and emits data events with parsed 37342 * AAC Audio Frames of the individual packets. Input audio in ADTS 37343 * format is unpacked and re-emitted as AAC frames. 37344 * 37345 * @see http://wiki.multimedia.cx/index.php?title=ADTS 37346 * @see http://wiki.multimedia.cx/?title=Understanding_AAC 37347 */ 37348AdtsStream = function() { 37349 var buffer; 37350 37351 AdtsStream.prototype.init.call(this); 37352 37353 this.push = function(packet) { 37354 var 37355 i = 0, 37356 frameNum = 0, 37357 frameLength, 37358 protectionSkipBytes, 37359 frameEnd, 37360 oldBuffer, 37361 sampleCount, 37362 adtsFrameDuration; 37363 37364 if (packet.type !== 'audio') { 37365 // ignore non-audio data 37366 return; 37367 } 37368 37369 // Prepend any data in the buffer to the input data so that we can parse 37370 // aac frames the cross a PES packet boundary 37371 if (buffer) { 37372 oldBuffer = buffer; 37373 buffer = new Uint8Array(oldBuffer.byteLength + packet.data.byteLength); 37374 buffer.set(oldBuffer); 37375 buffer.set(packet.data, oldBuffer.byteLength); 37376 } else { 37377 buffer = packet.data; 37378 } 37379 37380 // unpack any ADTS frames which have been fully received 37381 // for details on the ADTS header, see http://wiki.multimedia.cx/index.php?title=ADTS 37382 while (i + 5 < buffer.length) { 37383 37384 // Loook for the start of an ADTS header.. 37385 if (buffer[i] !== 0xFF || (buffer[i + 1] & 0xF6) !== 0xF0) { 37386 // If a valid header was not found, jump one forward and attempt to 37387 // find a valid ADTS header starting at the next byte 37388 i++; 37389 continue; 37390 } 37391 37392 // The protection skip bit tells us if we have 2 bytes of CRC data at the 37393 // end of the ADTS header 37394 protectionSkipBytes = (~buffer[i + 1] & 0x01) * 2; 37395 37396 // Frame length is a 13 bit integer starting 16 bits from the 37397 // end of the sync sequence 37398 frameLength = ((buffer[i + 3] & 0x03) << 11) | 37399 (buffer[i + 4] << 3) | 37400 ((buffer[i + 5] & 0xe0) >> 5); 37401 37402 sampleCount = ((buffer[i + 6] & 0x03) + 1) * 1024; 37403 adtsFrameDuration = (sampleCount * 90000) / 37404 ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2]; 37405 37406 frameEnd = i + frameLength; 37407 37408 // If we don't have enough data to actually finish this ADTS frame, return 37409 // and wait for more data 37410 if (buffer.byteLength < frameEnd) { 37411 return; 37412 } 37413 37414 // Otherwise, deliver the complete AAC frame 37415 this.trigger('data', { 37416 pts: packet.pts + (frameNum * adtsFrameDuration), 37417 dts: packet.dts + (frameNum * adtsFrameDuration), 37418 sampleCount: sampleCount, 37419 audioobjecttype: ((buffer[i + 2] >>> 6) & 0x03) + 1, 37420 channelcount: ((buffer[i + 2] & 1) << 2) | 37421 ((buffer[i + 3] & 0xc0) >>> 6), 37422 samplerate: ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2], 37423 samplingfrequencyindex: (buffer[i + 2] & 0x3c) >>> 2, 37424 // assume ISO/IEC 14496-12 AudioSampleEntry default of 16 37425 samplesize: 16, 37426 data: buffer.subarray(i + 7 + protectionSkipBytes, frameEnd) 37427 }); 37428 37429 // If the buffer is empty, clear it and return 37430 if (buffer.byteLength === frameEnd) { 37431 buffer = undefined; 37432 return; 37433 } 37434 37435 frameNum++; 37436 37437 // Remove the finished frame from the buffer and start the process again 37438 buffer = buffer.subarray(frameEnd); 37439 } 37440 }; 37441 this.flush = function() { 37442 this.trigger('done'); 37443 }; 37444}; 37445 37446AdtsStream.prototype = new Stream(); 37447 37448module.exports = AdtsStream; 37449 37450},{"../utils/stream.js":62}],41:[function(require,module,exports){ 37451'use strict'; 37452 37453var Stream = require('../utils/stream.js'); 37454var ExpGolomb = require('../utils/exp-golomb.js'); 37455 37456var H264Stream, NalByteStream; 37457var PROFILES_WITH_OPTIONAL_SPS_DATA; 37458 37459/** 37460 * Accepts a NAL unit byte stream and unpacks the embedded NAL units. 37461 */ 37462NalByteStream = function() { 37463 var 37464 syncPoint = 0, 37465 i, 37466 buffer; 37467 NalByteStream.prototype.init.call(this); 37468 37469 this.push = function(data) { 37470 var swapBuffer; 37471 37472 if (!buffer) { 37473 buffer = data.data; 37474 } else { 37475 swapBuffer = new Uint8Array(buffer.byteLength + data.data.byteLength); 37476 swapBuffer.set(buffer); 37477 swapBuffer.set(data.data, buffer.byteLength); 37478 buffer = swapBuffer; 37479 } 37480 37481 // Rec. ITU-T H.264, Annex B 37482 // scan for NAL unit boundaries 37483 37484 // a match looks like this: 37485 // 0 0 1 .. NAL .. 0 0 1 37486 // ^ sync point ^ i 37487 // or this: 37488 // 0 0 1 .. NAL .. 0 0 0 37489 // ^ sync point ^ i 37490 37491 // advance the sync point to a NAL start, if necessary 37492 for (; syncPoint < buffer.byteLength - 3; syncPoint++) { 37493 if (buffer[syncPoint + 2] === 1) { 37494 // the sync point is properly aligned 37495 i = syncPoint + 5; 37496 break; 37497 } 37498 } 37499 37500 while (i < buffer.byteLength) { 37501 // look at the current byte to determine if we've hit the end of 37502 // a NAL unit boundary 37503 switch (buffer[i]) { 37504 case 0: 37505 // skip past non-sync sequences 37506 if (buffer[i - 1] !== 0) { 37507 i += 2; 37508 break; 37509 } else if (buffer[i - 2] !== 0) { 37510 i++; 37511 break; 37512 } 37513 37514 // deliver the NAL unit if it isn't empty 37515 if (syncPoint + 3 !== i - 2) { 37516 this.trigger('data', buffer.subarray(syncPoint + 3, i - 2)); 37517 } 37518 37519 // drop trailing zeroes 37520 do { 37521 i++; 37522 } while (buffer[i] !== 1 && i < buffer.length); 37523 syncPoint = i - 2; 37524 i += 3; 37525 break; 37526 case 1: 37527 // skip past non-sync sequences 37528 if (buffer[i - 1] !== 0 || 37529 buffer[i - 2] !== 0) { 37530 i += 3; 37531 break; 37532 } 37533 37534 // deliver the NAL unit 37535 this.trigger('data', buffer.subarray(syncPoint + 3, i - 2)); 37536 syncPoint = i - 2; 37537 i += 3; 37538 break; 37539 default: 37540 // the current byte isn't a one or zero, so it cannot be part 37541 // of a sync sequence 37542 i += 3; 37543 break; 37544 } 37545 } 37546 // filter out the NAL units that were delivered 37547 buffer = buffer.subarray(syncPoint); 37548 i -= syncPoint; 37549 syncPoint = 0; 37550 }; 37551 37552 this.flush = function() { 37553 // deliver the last buffered NAL unit 37554 if (buffer && buffer.byteLength > 3) { 37555 this.trigger('data', buffer.subarray(syncPoint + 3)); 37556 } 37557 // reset the stream state 37558 buffer = null; 37559 syncPoint = 0; 37560 this.trigger('done'); 37561 }; 37562}; 37563NalByteStream.prototype = new Stream(); 37564 37565// values of profile_idc that indicate additional fields are included in the SPS 37566// see Recommendation ITU-T H.264 (4/2013), 37567// 7.3.2.1.1 Sequence parameter set data syntax 37568PROFILES_WITH_OPTIONAL_SPS_DATA = { 37569 100: true, 37570 110: true, 37571 122: true, 37572 244: true, 37573 44: true, 37574 83: true, 37575 86: true, 37576 118: true, 37577 128: true, 37578 138: true, 37579 139: true, 37580 134: true 37581}; 37582 37583/** 37584 * Accepts input from a ElementaryStream and produces H.264 NAL unit data 37585 * events. 37586 */ 37587H264Stream = function() { 37588 var 37589 nalByteStream = new NalByteStream(), 37590 self, 37591 trackId, 37592 currentPts, 37593 currentDts, 37594 37595 discardEmulationPreventionBytes, 37596 readSequenceParameterSet, 37597 skipScalingList; 37598 37599 H264Stream.prototype.init.call(this); 37600 self = this; 37601 37602 this.push = function(packet) { 37603 if (packet.type !== 'video') { 37604 return; 37605 } 37606 trackId = packet.trackId; 37607 currentPts = packet.pts; 37608 currentDts = packet.dts; 37609 37610 nalByteStream.push(packet); 37611 }; 37612 37613 nalByteStream.on('data', function(data) { 37614 var 37615 event = { 37616 trackId: trackId, 37617 pts: currentPts, 37618 dts: currentDts, 37619 data: data 37620 }; 37621 37622 switch (data[0] & 0x1f) { 37623 case 0x05: 37624 event.nalUnitType = 'slice_layer_without_partitioning_rbsp_idr'; 37625 break; 37626 case 0x06: 37627 event.nalUnitType = 'sei_rbsp'; 37628 event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1)); 37629 break; 37630 case 0x07: 37631 event.nalUnitType = 'seq_parameter_set_rbsp'; 37632 event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1)); 37633 event.config = readSequenceParameterSet(event.escapedRBSP); 37634 break; 37635 case 0x08: 37636 event.nalUnitType = 'pic_parameter_set_rbsp'; 37637 break; 37638 case 0x09: 37639 event.nalUnitType = 'access_unit_delimiter_rbsp'; 37640 break; 37641 37642 default: 37643 break; 37644 } 37645 self.trigger('data', event); 37646 }); 37647 nalByteStream.on('done', function() { 37648 self.trigger('done'); 37649 }); 37650 37651 this.flush = function() { 37652 nalByteStream.flush(); 37653 }; 37654 37655 /** 37656 * Advance the ExpGolomb decoder past a scaling list. The scaling 37657 * list is optionally transmitted as part of a sequence parameter 37658 * set and is not relevant to transmuxing. 37659 * @param count {number} the number of entries in this scaling list 37660 * @param expGolombDecoder {object} an ExpGolomb pointed to the 37661 * start of a scaling list 37662 * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1 37663 */ 37664 skipScalingList = function(count, expGolombDecoder) { 37665 var 37666 lastScale = 8, 37667 nextScale = 8, 37668 j, 37669 deltaScale; 37670 37671 for (j = 0; j < count; j++) { 37672 if (nextScale !== 0) { 37673 deltaScale = expGolombDecoder.readExpGolomb(); 37674 nextScale = (lastScale + deltaScale + 256) % 256; 37675 } 37676 37677 lastScale = (nextScale === 0) ? lastScale : nextScale; 37678 } 37679 }; 37680 37681 /** 37682 * Expunge any "Emulation Prevention" bytes from a "Raw Byte 37683 * Sequence Payload" 37684 * @param data {Uint8Array} the bytes of a RBSP from a NAL 37685 * unit 37686 * @return {Uint8Array} the RBSP without any Emulation 37687 * Prevention Bytes 37688 */ 37689 discardEmulationPreventionBytes = function(data) { 37690 var 37691 length = data.byteLength, 37692 emulationPreventionBytesPositions = [], 37693 i = 1, 37694 newLength, newData; 37695 37696 // Find all `Emulation Prevention Bytes` 37697 while (i < length - 2) { 37698 if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) { 37699 emulationPreventionBytesPositions.push(i + 2); 37700 i += 2; 37701 } else { 37702 i++; 37703 } 37704 } 37705 37706 // If no Emulation Prevention Bytes were found just return the original 37707 // array 37708 if (emulationPreventionBytesPositions.length === 0) { 37709 return data; 37710 } 37711 37712 // Create a new array to hold the NAL unit data 37713 newLength = length - emulationPreventionBytesPositions.length; 37714 newData = new Uint8Array(newLength); 37715 var sourceIndex = 0; 37716 37717 for (i = 0; i < newLength; sourceIndex++, i++) { 37718 if (sourceIndex === emulationPreventionBytesPositions[0]) { 37719 // Skip this byte 37720 sourceIndex++; 37721 // Remove this position index 37722 emulationPreventionBytesPositions.shift(); 37723 } 37724 newData[i] = data[sourceIndex]; 37725 } 37726 37727 return newData; 37728 }; 37729 37730 /** 37731 * Read a sequence parameter set and return some interesting video 37732 * properties. A sequence parameter set is the H264 metadata that 37733 * describes the properties of upcoming video frames. 37734 * @param data {Uint8Array} the bytes of a sequence parameter set 37735 * @return {object} an object with configuration parsed from the 37736 * sequence parameter set, including the dimensions of the 37737 * associated video frames. 37738 */ 37739 readSequenceParameterSet = function(data) { 37740 var 37741 frameCropLeftOffset = 0, 37742 frameCropRightOffset = 0, 37743 frameCropTopOffset = 0, 37744 frameCropBottomOffset = 0, 37745 sarScale = 1, 37746 expGolombDecoder, profileIdc, levelIdc, profileCompatibility, 37747 chromaFormatIdc, picOrderCntType, 37748 numRefFramesInPicOrderCntCycle, picWidthInMbsMinus1, 37749 picHeightInMapUnitsMinus1, 37750 frameMbsOnlyFlag, 37751 scalingListCount, 37752 sarRatio, 37753 aspectRatioIdc, 37754 i; 37755 37756 expGolombDecoder = new ExpGolomb(data); 37757 profileIdc = expGolombDecoder.readUnsignedByte(); // profile_idc 37758 profileCompatibility = expGolombDecoder.readUnsignedByte(); // constraint_set[0-5]_flag 37759 levelIdc = expGolombDecoder.readUnsignedByte(); // level_idc u(8) 37760 expGolombDecoder.skipUnsignedExpGolomb(); // seq_parameter_set_id 37761 37762 // some profiles have more optional data we don't need 37763 if (PROFILES_WITH_OPTIONAL_SPS_DATA[profileIdc]) { 37764 chromaFormatIdc = expGolombDecoder.readUnsignedExpGolomb(); 37765 if (chromaFormatIdc === 3) { 37766 expGolombDecoder.skipBits(1); // separate_colour_plane_flag 37767 } 37768 expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_luma_minus8 37769 expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_chroma_minus8 37770 expGolombDecoder.skipBits(1); // qpprime_y_zero_transform_bypass_flag 37771 if (expGolombDecoder.readBoolean()) { // seq_scaling_matrix_present_flag 37772 scalingListCount = (chromaFormatIdc !== 3) ? 8 : 12; 37773 for (i = 0; i < scalingListCount; i++) { 37774 if (expGolombDecoder.readBoolean()) { // seq_scaling_list_present_flag[ i ] 37775 if (i < 6) { 37776 skipScalingList(16, expGolombDecoder); 37777 } else { 37778 skipScalingList(64, expGolombDecoder); 37779 } 37780 } 37781 } 37782 } 37783 } 37784 37785 expGolombDecoder.skipUnsignedExpGolomb(); // log2_max_frame_num_minus4 37786 picOrderCntType = expGolombDecoder.readUnsignedExpGolomb(); 37787 37788 if (picOrderCntType === 0) { 37789 expGolombDecoder.readUnsignedExpGolomb(); // log2_max_pic_order_cnt_lsb_minus4 37790 } else if (picOrderCntType === 1) { 37791 expGolombDecoder.skipBits(1); // delta_pic_order_always_zero_flag 37792 expGolombDecoder.skipExpGolomb(); // offset_for_non_ref_pic 37793 expGolombDecoder.skipExpGolomb(); // offset_for_top_to_bottom_field 37794 numRefFramesInPicOrderCntCycle = expGolombDecoder.readUnsignedExpGolomb(); 37795 for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) { 37796 expGolombDecoder.skipExpGolomb(); // offset_for_ref_frame[ i ] 37797 } 37798 } 37799 37800 expGolombDecoder.skipUnsignedExpGolomb(); // max_num_ref_frames 37801 expGolombDecoder.skipBits(1); // gaps_in_frame_num_value_allowed_flag 37802 37803 picWidthInMbsMinus1 = expGolombDecoder.readUnsignedExpGolomb(); 37804 picHeightInMapUnitsMinus1 = expGolombDecoder.readUnsignedExpGolomb(); 37805 37806 frameMbsOnlyFlag = expGolombDecoder.readBits(1); 37807 if (frameMbsOnlyFlag === 0) { 37808 expGolombDecoder.skipBits(1); // mb_adaptive_frame_field_flag 37809 } 37810 37811 expGolombDecoder.skipBits(1); // direct_8x8_inference_flag 37812 if (expGolombDecoder.readBoolean()) { // frame_cropping_flag 37813 frameCropLeftOffset = expGolombDecoder.readUnsignedExpGolomb(); 37814 frameCropRightOffset = expGolombDecoder.readUnsignedExpGolomb(); 37815 frameCropTopOffset = expGolombDecoder.readUnsignedExpGolomb(); 37816 frameCropBottomOffset = expGolombDecoder.readUnsignedExpGolomb(); 37817 } 37818 if (expGolombDecoder.readBoolean()) { 37819 // vui_parameters_present_flag 37820 if (expGolombDecoder.readBoolean()) { 37821 // aspect_ratio_info_present_flag 37822 aspectRatioIdc = expGolombDecoder.readUnsignedByte(); 37823 switch (aspectRatioIdc) { 37824 case 1: sarRatio = [1, 1]; break; 37825 case 2: sarRatio = [12, 11]; break; 37826 case 3: sarRatio = [10, 11]; break; 37827 case 4: sarRatio = [16, 11]; break; 37828 case 5: sarRatio = [40, 33]; break; 37829 case 6: sarRatio = [24, 11]; break; 37830 case 7: sarRatio = [20, 11]; break; 37831 case 8: sarRatio = [32, 11]; break; 37832 case 9: sarRatio = [80, 33]; break; 37833 case 10: sarRatio = [18, 11]; break; 37834 case 11: sarRatio = [15, 11]; break; 37835 case 12: sarRatio = [64, 33]; break; 37836 case 13: sarRatio = [160, 99]; break; 37837 case 14: sarRatio = [4, 3]; break; 37838 case 15: sarRatio = [3, 2]; break; 37839 case 16: sarRatio = [2, 1]; break; 37840 case 255: { 37841 sarRatio = [expGolombDecoder.readUnsignedByte() << 8 | 37842 expGolombDecoder.readUnsignedByte(), 37843 expGolombDecoder.readUnsignedByte() << 8 | 37844 expGolombDecoder.readUnsignedByte() ]; 37845 break; 37846 } 37847 } 37848 if (sarRatio) { 37849 sarScale = sarRatio[0] / sarRatio[1]; 37850 } 37851 } 37852 } 37853 return { 37854 profileIdc: profileIdc, 37855 levelIdc: levelIdc, 37856 profileCompatibility: profileCompatibility, 37857 width: Math.ceil((((picWidthInMbsMinus1 + 1) * 16) - frameCropLeftOffset * 2 - frameCropRightOffset * 2) * sarScale), 37858 height: ((2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16) - (frameCropTopOffset * 2) - (frameCropBottomOffset * 2) 37859 }; 37860 }; 37861 37862}; 37863H264Stream.prototype = new Stream(); 37864 37865module.exports = { 37866 H264Stream: H264Stream, 37867 NalByteStream: NalByteStream 37868}; 37869 37870},{"../utils/exp-golomb.js":61,"../utils/stream.js":62}],42:[function(require,module,exports){ 37871var highPrefix = [33, 16, 5, 32, 164, 27]; 37872var lowPrefix = [33, 65, 108, 84, 1, 2, 4, 8, 168, 2, 4, 8, 17, 191, 252]; 37873var zeroFill = function(count) { 37874 var a = []; 37875 while (count--) { 37876 a.push(0); 37877 } 37878 return a; 37879}; 37880 37881var makeTable = function(metaTable) { 37882 return Object.keys(metaTable).reduce(function(obj, key) { 37883 obj[key] = new Uint8Array(metaTable[key].reduce(function(arr, part) { 37884 return arr.concat(part); 37885 }, [])); 37886 return obj; 37887 }, {}); 37888}; 37889 37890// Frames-of-silence to use for filling in missing AAC frames
vendor: 4,575 bytes, lines 37891-38019
37891var coneOfSilence = { 37892 96000: [highPrefix, [227, 64], zeroFill(154), [56]], 37893 88200: [highPrefix, [231], zeroFill(170), [56]], 37894 64000: [highPrefix, [248, 192], zeroFill(240), [56]], 37895 48000: [highPrefix, [255, 192], zeroFill(268), [55, 148, 128], zeroFill(54), [112]], 37896 44100: [highPrefix, [255, 192], zeroFill(268), [55, 163, 128], zeroFill(84), [112]], 37897 32000: [highPrefix, [255, 192], zeroFill(268), [55, 234], zeroFill(226), [112]], 37898 24000: [highPrefix, [255, 192], zeroFill(268), [55, 255, 128], zeroFill(268), [111, 112], zeroFill(126), [224]], 37899 16000: [highPrefix, [255, 192], zeroFill(268), [55, 255, 128], zeroFill(268), [111, 255], zeroFill(269), [223, 108], zeroFill(195), [1, 192]], 37900 12000: [lowPrefix, zeroFill(268), [3, 127, 248], zeroFill(268), [6, 255, 240], zeroFill(268), [13, 255, 224], zeroFill(268), [27, 253, 128], zeroFill(259), [56]], 37901 11025: [lowPrefix, zeroFill(268), [3, 127, 248], zeroFill(268), [6, 255, 240], zeroFill(268), [13, 255, 224], zeroFill(268), [27, 255, 192], zeroFill(268), [55, 175, 128], zeroFill(108), [112]], 37902 8000: [lowPrefix, zeroFill(268), [3, 121, 16], zeroFill(47), [7]] 37903}; 37904 37905module.exports = makeTable(coneOfSilence); 37906 37907},{}],43:[function(require,module,exports){ 37908'use strict'; 37909 37910var Stream = require('../utils/stream.js'); 37911 37912/** 37913 * The final stage of the transmuxer that emits the flv tags 37914 * for audio, video, and metadata. Also tranlates in time and 37915 * outputs caption data and id3 cues. 37916 */ 37917var CoalesceStream = function(options) { 37918 // Number of Tracks per output segment 37919 // If greater than 1, we combine multiple 37920 // tracks into a single segment 37921 this.numberOfTracks = 0; 37922 this.metadataStream = options.metadataStream; 37923 37924 this.videoTags = []; 37925 this.audioTags = []; 37926 this.videoTrack = null; 37927 this.audioTrack = null; 37928 this.pendingCaptions = []; 37929 this.pendingMetadata = []; 37930 this.pendingTracks = 0; 37931 this.processedTracks = 0; 37932 37933 CoalesceStream.prototype.init.call(this); 37934 37935 // Take output from multiple 37936 this.push = function(output) { 37937 // buffer incoming captions until the associated video segment 37938 // finishes 37939 if (output.text) { 37940 return this.pendingCaptions.push(output); 37941 } 37942 // buffer incoming id3 tags until the final flush 37943 if (output.frames) { 37944 return this.pendingMetadata.push(output); 37945 } 37946 37947 if (output.track.type === 'video') { 37948 this.videoTrack = output.track; 37949 this.videoTags = output.tags; 37950 this.pendingTracks++; 37951 } 37952 if (output.track.type === 'audio') { 37953 this.audioTrack = output.track; 37954 this.audioTags = output.tags; 37955 this.pendingTracks++; 37956 } 37957 }; 37958}; 37959 37960CoalesceStream.prototype = new Stream(); 37961CoalesceStream.prototype.flush = function(flushSource) { 37962 var 37963 id3, 37964 caption, 37965 i, 37966 timelineStartPts, 37967 event = { 37968 tags: {}, 37969 captions: [], 37970 captionStreams: {}, 37971 metadata: [] 37972 }; 37973 37974 if (this.pendingTracks < this.numberOfTracks) { 37975 if (flushSource !== 'VideoSegmentStream' && 37976 flushSource !== 'AudioSegmentStream') { 37977 // Return because we haven't received a flush from a data-generating 37978 // portion of the segment (meaning that we have only recieved meta-data 37979 // or captions.) 37980 return; 37981 } else if (this.pendingTracks === 0) { 37982 // In the case where we receive a flush without any data having been 37983 // received we consider it an emitted track for the purposes of coalescing 37984 // `done` events. 37985 // We do this for the case where there is an audio and video track in the 37986 // segment but no audio data. (seen in several playlists with alternate 37987 // audio tracks and no audio present in the main TS segments.) 37988 this.processedTracks++; 37989 37990 if (this.processedTracks < this.numberOfTracks) { 37991 return; 37992 } 37993 } 37994 } 37995 37996 this.processedTracks += this.pendingTracks; 37997 this.pendingTracks = 0; 37998 37999 if (this.processedTracks < this.numberOfTracks) { 38000 return; 38001 } 38002 38003 if (this.videoTrack) { 38004 timelineStartPts = this.videoTrack.timelineStartInfo.pts; 38005 } else if (this.audioTrack) { 38006 timelineStartPts = this.audioTrack.timelineStartInfo.pts; 38007 } 38008 38009 event.tags.videoTags = this.videoTags; 38010 event.tags.audioTags = this.audioTags; 38011 38012 // Translate caption PTS times into second offsets into the 38013 // video timeline for the segment, and add track info 38014 for (i = 0; i < this.pendingCaptions.length; i++) { 38015 caption = this.pendingCaptions[i]; 38016 caption.startTime = caption.startPts - timelineStartPts; 38017 caption.startTime /= 90e3; 38018 caption.endTime = caption.endPts - timelineStartPts; 38019 caption.endTime /= 90e3;
vendor: 28,064 bytes, lines 38020-38982
38020 event.captionStreams[caption.stream] = true; 38021 event.captions.push(caption); 38022 } 38023 38024 // Translate ID3 frame PTS times into second offsets into the 38025 // video timeline for the segment 38026 for (i = 0; i < this.pendingMetadata.length; i++) { 38027 id3 = this.pendingMetadata[i]; 38028 id3.cueTime = id3.pts - timelineStartPts; 38029 id3.cueTime /= 90e3; 38030 event.metadata.push(id3); 38031 } 38032 // We add this to every single emitted segment even though we only need 38033 // it for the first 38034 event.metadata.dispatchType = this.metadataStream.dispatchType; 38035 38036 // Reset stream state 38037 this.videoTrack = null; 38038 this.audioTrack = null; 38039 this.videoTags = []; 38040 this.audioTags = []; 38041 this.pendingCaptions.length = 0; 38042 this.pendingMetadata.length = 0; 38043 this.pendingTracks = 0; 38044 this.processedTracks = 0; 38045 38046 // Emit the final segment 38047 this.trigger('data', event); 38048 38049 this.trigger('done'); 38050}; 38051 38052module.exports = CoalesceStream; 38053 38054},{"../utils/stream.js":62}],44:[function(require,module,exports){ 38055'use strict'; 38056 38057var FlvTag = require('./flv-tag.js'); 38058 38059// For information on the FLV format, see 38060// http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf. 38061// Technically, this function returns the header and a metadata FLV tag 38062// if duration is greater than zero 38063// duration in seconds 38064// @return {object} the bytes of the FLV header as a Uint8Array 38065var getFlvHeader = function(duration, audio, video) { // :ByteArray { 38066 var 38067 headBytes = new Uint8Array(3 + 1 + 1 + 4), 38068 head = new DataView(headBytes.buffer), 38069 metadata, 38070 result, 38071 metadataLength; 38072 38073 // default arguments 38074 duration = duration || 0; 38075 audio = audio === undefined ? true : audio; 38076 video = video === undefined ? true : video; 38077 38078 // signature 38079 head.setUint8(0, 0x46); // 'F' 38080 head.setUint8(1, 0x4c); // 'L' 38081 head.setUint8(2, 0x56); // 'V' 38082 38083 // version 38084 head.setUint8(3, 0x01); 38085 38086 // flags 38087 head.setUint8(4, (audio ? 0x04 : 0x00) | (video ? 0x01 : 0x00)); 38088 38089 // data offset, should be 9 for FLV v1 38090 head.setUint32(5, headBytes.byteLength); 38091 38092 // init the first FLV tag 38093 if (duration <= 0) { 38094 // no duration available so just write the first field of the first 38095 // FLV tag 38096 result = new Uint8Array(headBytes.byteLength + 4); 38097 result.set(headBytes); 38098 result.set([0, 0, 0, 0], headBytes.byteLength); 38099 return result; 38100 } 38101 38102 // write out the duration metadata tag 38103 metadata = new FlvTag(FlvTag.METADATA_TAG); 38104 metadata.pts = metadata.dts = 0; 38105 metadata.writeMetaDataDouble('duration', duration); 38106 metadataLength = metadata.finalize().length; 38107 result = new Uint8Array(headBytes.byteLength + metadataLength); 38108 result.set(headBytes); 38109 result.set(head.byteLength, metadataLength); 38110 38111 return result; 38112}; 38113 38114module.exports = getFlvHeader; 38115 38116},{"./flv-tag.js":45}],45:[function(require,module,exports){ 38117/** 38118 * An object that stores the bytes of an FLV tag and methods for 38119 * querying and manipulating that data. 38120 * @see http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf 38121 */ 38122'use strict'; 38123 38124var FlvTag; 38125 38126// (type:uint, extraData:Boolean = false) extends ByteArray 38127FlvTag = function(type, extraData) { 38128 var 38129 // Counter if this is a metadata tag, nal start marker if this is a video 38130 // tag. unused if this is an audio tag 38131 adHoc = 0, // :uint 38132 38133 // The default size is 16kb but this is not enough to hold iframe 38134 // data and the resizing algorithm costs a bit so we create a larger 38135 // starting buffer for video tags 38136 bufferStartSize = 16384, 38137 38138 // checks whether the FLV tag has enough capacity to accept the proposed 38139 // write and re-allocates the internal buffers if necessary 38140 prepareWrite = function(flv, count) { 38141 var 38142 bytes, 38143 minLength = flv.position + count; 38144 if (minLength < flv.bytes.byteLength) { 38145 // there's enough capacity so do nothing 38146 return; 38147 } 38148 38149 // allocate a new buffer and copy over the data that will not be modified 38150 bytes = new Uint8Array(minLength * 2); 38151 bytes.set(flv.bytes.subarray(0, flv.position), 0); 38152 flv.bytes = bytes; 38153 flv.view = new DataView(flv.bytes.buffer); 38154 }, 38155 38156 // commonly used metadata properties 38157 widthBytes = FlvTag.widthBytes || new Uint8Array('width'.length), 38158 heightBytes = FlvTag.heightBytes || new Uint8Array('height'.length), 38159 videocodecidBytes = FlvTag.videocodecidBytes || new Uint8Array('videocodecid'.length), 38160 i; 38161 38162 if (!FlvTag.widthBytes) { 38163 // calculating the bytes of common metadata names ahead of time makes the 38164 // corresponding writes faster because we don't have to loop over the 38165 // characters 38166 // re-test with test/perf.html if you're planning on changing this 38167 for (i = 0; i < 'width'.length; i++) { 38168 widthBytes[i] = 'width'.charCodeAt(i); 38169 } 38170 for (i = 0; i < 'height'.length; i++) { 38171 heightBytes[i] = 'height'.charCodeAt(i); 38172 } 38173 for (i = 0; i < 'videocodecid'.length; i++) { 38174 videocodecidBytes[i] = 'videocodecid'.charCodeAt(i); 38175 } 38176 38177 FlvTag.widthBytes = widthBytes; 38178 FlvTag.heightBytes = heightBytes; 38179 FlvTag.videocodecidBytes = videocodecidBytes; 38180 } 38181 38182 this.keyFrame = false; // :Boolean 38183 38184 switch (type) { 38185 case FlvTag.VIDEO_TAG: 38186 this.length = 16; 38187 // Start the buffer at 256k 38188 bufferStartSize *= 6; 38189 break; 38190 case FlvTag.AUDIO_TAG: 38191 this.length = 13; 38192 this.keyFrame = true; 38193 break; 38194 case FlvTag.METADATA_TAG: 38195 this.length = 29; 38196 this.keyFrame = true; 38197 break; 38198 default: 38199 throw new Error('Unknown FLV tag type'); 38200 } 38201 38202 this.bytes = new Uint8Array(bufferStartSize); 38203 this.view = new DataView(this.bytes.buffer); 38204 this.bytes[0] = type; 38205 this.position = this.length; 38206 this.keyFrame = extraData; // Defaults to false 38207 38208 // presentation timestamp 38209 this.pts = 0; 38210 // decoder timestamp 38211 this.dts = 0; 38212 38213 // ByteArray#writeBytes(bytes:ByteArray, offset:uint = 0, length:uint = 0) 38214 this.writeBytes = function(bytes, offset, length) { 38215 var 38216 start = offset || 0, 38217 end; 38218 length = length || bytes.byteLength; 38219 end = start + length; 38220 38221 prepareWrite(this, length); 38222 this.bytes.set(bytes.subarray(start, end), this.position); 38223 38224 this.position += length; 38225 this.length = Math.max(this.length, this.position); 38226 }; 38227 38228 // ByteArray#writeByte(value:int):void 38229 this.writeByte = function(byte) { 38230 prepareWrite(this, 1); 38231 this.bytes[this.position] = byte; 38232 this.position++; 38233 this.length = Math.max(this.length, this.position); 38234 }; 38235 38236 // ByteArray#writeShort(value:int):void 38237 this.writeShort = function(short) { 38238 prepareWrite(this, 2); 38239 this.view.setUint16(this.position, short); 38240 this.position += 2; 38241 this.length = Math.max(this.length, this.position); 38242 }; 38243 38244 // Negative index into array 38245 // (pos:uint):int 38246 this.negIndex = function(pos) { 38247 return this.bytes[this.length - pos]; 38248 }; 38249 38250 // The functions below ONLY work when this[0] == VIDEO_TAG. 38251 // We are not going to check for that because we dont want the overhead 38252 // (nal:ByteArray = null):int 38253 this.nalUnitSize = function() { 38254 if (adHoc === 0) { 38255 return 0; 38256 } 38257 38258 return this.length - (adHoc + 4); 38259 }; 38260 38261 this.startNalUnit = function() { 38262 // remember position and add 4 bytes 38263 if (adHoc > 0) { 38264 throw new Error('Attempted to create new NAL wihout closing the old one'); 38265 } 38266 38267 // reserve 4 bytes for nal unit size 38268 adHoc = this.length; 38269 this.length += 4; 38270 this.position = this.length; 38271 }; 38272 38273 // (nal:ByteArray = null):void 38274 this.endNalUnit = function(nalContainer) { 38275 var 38276 nalStart, // :uint 38277 nalLength; // :uint 38278 38279 // Rewind to the marker and write the size 38280 if (this.length === adHoc + 4) { 38281 // we started a nal unit, but didnt write one, so roll back the 4 byte size value 38282 this.length -= 4; 38283 } else if (adHoc > 0) { 38284 nalStart = adHoc + 4; 38285 nalLength = this.length - nalStart; 38286 38287 this.position = adHoc; 38288 this.view.setUint32(this.position, nalLength); 38289 this.position = this.length; 38290 38291 if (nalContainer) { 38292 // Add the tag to the NAL unit 38293 nalContainer.push(this.bytes.subarray(nalStart, nalStart + nalLength)); 38294 } 38295 } 38296 38297 adHoc = 0; 38298 }; 38299 38300 /** 38301 * Write out a 64-bit floating point valued metadata property. This method is 38302 * called frequently during a typical parse and needs to be fast. 38303 */ 38304 // (key:String, val:Number):void 38305 this.writeMetaDataDouble = function(key, val) { 38306 var i; 38307 prepareWrite(this, 2 + key.length + 9); 38308 38309 // write size of property name 38310 this.view.setUint16(this.position, key.length); 38311 this.position += 2; 38312 38313 // this next part looks terrible but it improves parser throughput by 38314 // 10kB/s in my testing 38315 38316 // write property name 38317 if (key === 'width') { 38318 this.bytes.set(widthBytes, this.position); 38319 this.position += 5; 38320 } else if (key === 'height') { 38321 this.bytes.set(heightBytes, this.position); 38322 this.position += 6; 38323 } else if (key === 'videocodecid') { 38324 this.bytes.set(videocodecidBytes, this.position); 38325 this.position += 12; 38326 } else { 38327 for (i = 0; i < key.length; i++) { 38328 this.bytes[this.position] = key.charCodeAt(i); 38329 this.position++; 38330 } 38331 } 38332 38333 // skip null byte 38334 this.position++; 38335 38336 // write property value 38337 this.view.setFloat64(this.position, val); 38338 this.position += 8; 38339 38340 // update flv tag length 38341 this.length = Math.max(this.length, this.position); 38342 ++adHoc; 38343 }; 38344 38345 // (key:String, val:Boolean):void 38346 this.writeMetaDataBoolean = function(key, val) { 38347 var i; 38348 prepareWrite(this, 2); 38349 this.view.setUint16(this.position, key.length); 38350 this.position += 2; 38351 for (i = 0; i < key.length; i++) { 38352 // if key.charCodeAt(i) >= 255, handle error 38353 prepareWrite(this, 1); 38354 this.bytes[this.position] = key.charCodeAt(i); 38355 this.position++; 38356 } 38357 prepareWrite(this, 2); 38358 this.view.setUint8(this.position, 0x01); 38359 this.position++; 38360 this.view.setUint8(this.position, val ? 0x01 : 0x00); 38361 this.position++; 38362 this.length = Math.max(this.length, this.position); 38363 ++adHoc; 38364 }; 38365 38366 // ():ByteArray 38367 this.finalize = function() { 38368 var 38369 dtsDelta, // :int 38370 len; // :int 38371 38372 switch (this.bytes[0]) { 38373 // Video Data 38374 case FlvTag.VIDEO_TAG: 38375 // We only support AVC, 1 = key frame (for AVC, a seekable 38376 // frame), 2 = inter frame (for AVC, a non-seekable frame) 38377 this.bytes[11] = ((this.keyFrame || extraData) ? 0x10 : 0x20) | 0x07; 38378 this.bytes[12] = extraData ? 0x00 : 0x01; 38379 38380 dtsDelta = this.pts - this.dts; 38381 this.bytes[13] = (dtsDelta & 0x00FF0000) >>> 16; 38382 this.bytes[14] = (dtsDelta & 0x0000FF00) >>> 8; 38383 this.bytes[15] = (dtsDelta & 0x000000FF) >>> 0; 38384 break; 38385 38386 case FlvTag.AUDIO_TAG: 38387 this.bytes[11] = 0xAF; // 44 kHz, 16-bit stereo 38388 this.bytes[12] = extraData ? 0x00 : 0x01; 38389 break; 38390 38391 case FlvTag.METADATA_TAG: 38392 this.position = 11; 38393 this.view.setUint8(this.position, 0x02); // String type 38394 this.position++; 38395 this.view.setUint16(this.position, 0x0A); // 10 Bytes 38396 this.position += 2; 38397 // set "onMetaData" 38398 this.bytes.set([0x6f, 0x6e, 0x4d, 0x65, 38399 0x74, 0x61, 0x44, 0x61, 38400 0x74, 0x61], this.position); 38401 this.position += 10; 38402 this.bytes[this.position] = 0x08; // Array type 38403 this.position++; 38404 this.view.setUint32(this.position, adHoc); 38405 this.position = this.length; 38406 this.bytes.set([0, 0, 9], this.position); 38407 this.position += 3; // End Data Tag 38408 this.length = this.position; 38409 break; 38410 } 38411 38412 len = this.length - 11; 38413 38414 // write the DataSize field 38415 this.bytes[ 1] = (len & 0x00FF0000) >>> 16; 38416 this.bytes[ 2] = (len & 0x0000FF00) >>> 8; 38417 this.bytes[ 3] = (len & 0x000000FF) >>> 0; 38418 // write the Timestamp 38419 this.bytes[ 4] = (this.dts & 0x00FF0000) >>> 16; 38420 this.bytes[ 5] = (this.dts & 0x0000FF00) >>> 8; 38421 this.bytes[ 6] = (this.dts & 0x000000FF) >>> 0; 38422 this.bytes[ 7] = (this.dts & 0xFF000000) >>> 24; 38423 // write the StreamID 38424 this.bytes[ 8] = 0; 38425 this.bytes[ 9] = 0; 38426 this.bytes[10] = 0; 38427 38428 // Sometimes we're at the end of the view and have one slot to write a 38429 // uint32, so, prepareWrite of count 4, since, view is uint8 38430 prepareWrite(this, 4); 38431 this.view.setUint32(this.length, this.length); 38432 this.length += 4; 38433 this.position += 4; 38434 38435 // trim down the byte buffer to what is actually being used 38436 this.bytes = this.bytes.subarray(0, this.length); 38437 this.frameTime = FlvTag.frameTime(this.bytes); 38438 // if bytes.bytelength isn't equal to this.length, handle error 38439 return this; 38440 }; 38441}; 38442 38443FlvTag.AUDIO_TAG = 0x08; // == 8, :uint 38444FlvTag.VIDEO_TAG = 0x09; // == 9, :uint 38445FlvTag.METADATA_TAG = 0x12; // == 18, :uint 38446 38447// (tag:ByteArray):Boolean { 38448FlvTag.isAudioFrame = function(tag) { 38449 return FlvTag.AUDIO_TAG === tag[0]; 38450}; 38451 38452// (tag:ByteArray):Boolean { 38453FlvTag.isVideoFrame = function(tag) { 38454 return FlvTag.VIDEO_TAG === tag[0]; 38455}; 38456 38457// (tag:ByteArray):Boolean { 38458FlvTag.isMetaData = function(tag) { 38459 return FlvTag.METADATA_TAG === tag[0]; 38460}; 38461 38462// (tag:ByteArray):Boolean { 38463FlvTag.isKeyFrame = function(tag) { 38464 if (FlvTag.isVideoFrame(tag)) { 38465 return tag[11] === 0x17; 38466 } 38467 38468 if (FlvTag.isAudioFrame(tag)) { 38469 return true; 38470 } 38471 38472 if (FlvTag.isMetaData(tag)) { 38473 return true; 38474 } 38475 38476 return false; 38477}; 38478 38479// (tag:ByteArray):uint { 38480FlvTag.frameTime = function(tag) { 38481 var pts = tag[ 4] << 16; // :uint 38482 pts |= tag[ 5] << 8; 38483 pts |= tag[ 6] << 0; 38484 pts |= tag[ 7] << 24; 38485 return pts; 38486}; 38487 38488module.exports = FlvTag; 38489 38490},{}],46:[function(require,module,exports){ 38491module.exports = { 38492 tag: require('./flv-tag'), 38493 Transmuxer: require('./transmuxer'), 38494 getFlvHeader: require('./flv-header') 38495}; 38496 38497},{"./flv-header":44,"./flv-tag":45,"./transmuxer":48}],47:[function(require,module,exports){ 38498'use strict'; 38499 38500var TagList = function() { 38501 var self = this; 38502 38503 this.list = []; 38504 38505 this.push = function(tag) { 38506 this.list.push({ 38507 bytes: tag.bytes, 38508 dts: tag.dts, 38509 pts: tag.pts, 38510 keyFrame: tag.keyFrame, 38511 metaDataTag: tag.metaDataTag 38512 }); 38513 }; 38514 38515 Object.defineProperty(this, 'length', { 38516 get: function() { 38517 return self.list.length; 38518 } 38519 }); 38520}; 38521 38522module.exports = TagList; 38523 38524},{}],48:[function(require,module,exports){ 38525'use strict'; 38526 38527var Stream = require('../utils/stream.js'); 38528var FlvTag = require('./flv-tag.js'); 38529var m2ts = require('../m2ts/m2ts.js'); 38530var AdtsStream = require('../codecs/adts.js'); 38531var H264Stream = require('../codecs/h264').H264Stream; 38532var CoalesceStream = require('./coalesce-stream.js'); 38533var TagList = require('./tag-list.js'); 38534 38535var 38536 Transmuxer, 38537 VideoSegmentStream, 38538 AudioSegmentStream, 38539 collectTimelineInfo, 38540 metaDataTag, 38541 extraDataTag; 38542 38543/** 38544 * Store information about the start and end of the tracka and the 38545 * duration for each frame/sample we process in order to calculate 38546 * the baseMediaDecodeTime 38547 */ 38548collectTimelineInfo = function(track, data) { 38549 if (typeof data.pts === 'number') { 38550 if (track.timelineStartInfo.pts === undefined) { 38551 track.timelineStartInfo.pts = data.pts; 38552 } else { 38553 track.timelineStartInfo.pts = 38554 Math.min(track.timelineStartInfo.pts, data.pts); 38555 } 38556 } 38557 38558 if (typeof data.dts === 'number') { 38559 if (track.timelineStartInfo.dts === undefined) { 38560 track.timelineStartInfo.dts = data.dts; 38561 } else { 38562 track.timelineStartInfo.dts = 38563 Math.min(track.timelineStartInfo.dts, data.dts); 38564 } 38565 } 38566}; 38567 38568metaDataTag = function(track, pts) { 38569 var 38570 tag = new FlvTag(FlvTag.METADATA_TAG); // :FlvTag 38571 38572 tag.dts = pts; 38573 tag.pts = pts; 38574 38575 tag.writeMetaDataDouble('videocodecid', 7); 38576 tag.writeMetaDataDouble('width', track.width); 38577 tag.writeMetaDataDouble('height', track.height); 38578 38579 return tag; 38580}; 38581 38582extraDataTag = function(track, pts) { 38583 var 38584 i, 38585 tag = new FlvTag(FlvTag.VIDEO_TAG, true); 38586 38587 tag.dts = pts; 38588 tag.pts = pts; 38589 38590 tag.writeByte(0x01);// version 38591 tag.writeByte(track.profileIdc);// profile 38592 tag.writeByte(track.profileCompatibility);// compatibility 38593 tag.writeByte(track.levelIdc);// level 38594 tag.writeByte(0xFC | 0x03); // reserved (6 bits), NULA length size - 1 (2 bits) 38595 tag.writeByte(0xE0 | 0x01); // reserved (3 bits), num of SPS (5 bits) 38596 tag.writeShort(track.sps[0].length); // data of SPS 38597 tag.writeBytes(track.sps[0]); // SPS 38598 38599 tag.writeByte(track.pps.length); // num of PPS (will there ever be more that 1 PPS?) 38600 for (i = 0; i < track.pps.length; ++i) { 38601 tag.writeShort(track.pps[i].length); // 2 bytes for length of PPS 38602 tag.writeBytes(track.pps[i]); // data of PPS 38603 } 38604 38605 return tag; 38606}; 38607 38608/** 38609 * Constructs a single-track, media segment from AAC data 38610 * events. The output of this stream can be fed to flash. 38611 */ 38612AudioSegmentStream = function(track) { 38613 var 38614 adtsFrames = [], 38615 videoKeyFrames = [], 38616 oldExtraData; 38617 38618 AudioSegmentStream.prototype.init.call(this); 38619 38620 this.push = function(data) { 38621 collectTimelineInfo(track, data); 38622 38623 if (track) { 38624 track.audioobjecttype = data.audioobjecttype; 38625 track.channelcount = data.channelcount; 38626 track.samplerate = data.samplerate; 38627 track.samplingfrequencyindex = data.samplingfrequencyindex; 38628 track.samplesize = data.samplesize; 38629 track.extraData = (track.audioobjecttype << 11) | 38630 (track.samplingfrequencyindex << 7) | 38631 (track.channelcount << 3); 38632 } 38633 38634 data.pts = Math.round(data.pts / 90); 38635 data.dts = Math.round(data.dts / 90); 38636 38637 // buffer audio data until end() is called 38638 adtsFrames.push(data); 38639 }; 38640 38641 this.flush = function() { 38642 var currentFrame, adtsFrame, lastMetaPts, tags = new TagList(); 38643 // return early if no audio data has been observed 38644 if (adtsFrames.length === 0) { 38645 this.trigger('done', 'AudioSegmentStream'); 38646 return; 38647 } 38648 38649 lastMetaPts = -Infinity; 38650 38651 while (adtsFrames.length) { 38652 currentFrame = adtsFrames.shift(); 38653 38654 // write out a metadata frame at every video key frame 38655 if (videoKeyFrames.length && currentFrame.pts >= videoKeyFrames[0]) { 38656 lastMetaPts = videoKeyFrames.shift(); 38657 this.writeMetaDataTags(tags, lastMetaPts); 38658 } 38659 38660 // also write out metadata tags every 1 second so that the decoder 38661 // is re-initialized quickly after seeking into a different 38662 // audio configuration. 38663 if (track.extraData !== oldExtraData || currentFrame.pts - lastMetaPts >= 1000) { 38664 this.writeMetaDataTags(tags, currentFrame.pts); 38665 oldExtraData = track.extraData; 38666 lastMetaPts = currentFrame.pts; 38667 } 38668 38669 adtsFrame = new FlvTag(FlvTag.AUDIO_TAG); 38670 adtsFrame.pts = currentFrame.pts; 38671 adtsFrame.dts = currentFrame.dts; 38672 38673 adtsFrame.writeBytes(currentFrame.data); 38674 38675 tags.push(adtsFrame.finalize()); 38676 } 38677 38678 videoKeyFrames.length = 0; 38679 oldExtraData = null; 38680 this.trigger('data', {track: track, tags: tags.list}); 38681 38682 this.trigger('done', 'AudioSegmentStream'); 38683 }; 38684 38685 this.writeMetaDataTags = function(tags, pts) { 38686 var adtsFrame; 38687 38688 adtsFrame = new FlvTag(FlvTag.METADATA_TAG); 38689 // For audio, DTS is always the same as PTS. We want to set the DTS 38690 // however so we can compare with video DTS to determine approximate 38691 // packet order 38692 adtsFrame.pts = pts; 38693 adtsFrame.dts = pts; 38694 38695 // AAC is always 10 38696 adtsFrame.writeMetaDataDouble('audiocodecid', 10); 38697 adtsFrame.writeMetaDataBoolean('stereo', track.channelcount === 2); 38698 adtsFrame.writeMetaDataDouble('audiosamplerate', track.samplerate); 38699 // Is AAC always 16 bit? 38700 adtsFrame.writeMetaDataDouble('audiosamplesize', 16); 38701 38702 tags.push(adtsFrame.finalize()); 38703 38704 adtsFrame = new FlvTag(FlvTag.AUDIO_TAG, true); 38705 // For audio, DTS is always the same as PTS. We want to set the DTS 38706 // however so we can compare with video DTS to determine approximate 38707 // packet order 38708 adtsFrame.pts = pts; 38709 adtsFrame.dts = pts; 38710 38711 adtsFrame.view.setUint16(adtsFrame.position, track.extraData); 38712 adtsFrame.position += 2; 38713 adtsFrame.length = Math.max(adtsFrame.length, adtsFrame.position); 38714 38715 tags.push(adtsFrame.finalize()); 38716 }; 38717 38718 this.onVideoKeyFrame = function(pts) { 38719 videoKeyFrames.push(pts); 38720 }; 38721}; 38722AudioSegmentStream.prototype = new Stream(); 38723 38724/** 38725 * Store FlvTags for the h264 stream 38726 * @param track {object} track metadata configuration 38727 */ 38728VideoSegmentStream = function(track) { 38729 var 38730 nalUnits = [], 38731 config, 38732 h264Frame; 38733 VideoSegmentStream.prototype.init.call(this); 38734 38735 this.finishFrame = function(tags, frame) { 38736 if (!frame) { 38737 return; 38738 } 38739 // Check if keyframe and the length of tags. 38740 // This makes sure we write metadata on the first frame of a segment. 38741 if (config && track && track.newMetadata && 38742 (frame.keyFrame || tags.length === 0)) { 38743 // Push extra data on every IDR frame in case we did a stream change + seek 38744 var metaTag = metaDataTag(config, frame.dts).finalize(); 38745 var extraTag = extraDataTag(track, frame.dts).finalize(); 38746 38747 metaTag.metaDataTag = extraTag.metaDataTag = true; 38748 38749 tags.push(metaTag); 38750 tags.push(extraTag); 38751 track.newMetadata = false; 38752 38753 this.trigger('keyframe', frame.dts); 38754 } 38755 38756 frame.endNalUnit(); 38757 tags.push(frame.finalize()); 38758 h264Frame = null; 38759 }; 38760 38761 this.push = function(data) { 38762 collectTimelineInfo(track, data); 38763 38764 data.pts = Math.round(data.pts / 90); 38765 data.dts = Math.round(data.dts / 90); 38766 38767 // buffer video until flush() is called 38768 nalUnits.push(data); 38769 }; 38770 38771 this.flush = function() { 38772 var 38773 currentNal, 38774 tags = new TagList(); 38775 38776 // Throw away nalUnits at the start of the byte stream until we find 38777 // the first AUD 38778 while (nalUnits.length) { 38779 if (nalUnits[0].nalUnitType === 'access_unit_delimiter_rbsp') { 38780 break; 38781 } 38782 nalUnits.shift(); 38783 } 38784 38785 // return early if no video data has been observed 38786 if (nalUnits.length === 0) { 38787 this.trigger('done', 'VideoSegmentStream'); 38788 return; 38789 } 38790 38791 while (nalUnits.length) { 38792 currentNal = nalUnits.shift(); 38793 38794 // record the track config 38795 if (currentNal.nalUnitType === 'seq_parameter_set_rbsp') { 38796 track.newMetadata = true; 38797 config = currentNal.config; 38798 track.width = config.width; 38799 track.height = config.height; 38800 track.sps = [currentNal.data]; 38801 track.profileIdc = config.profileIdc; 38802 track.levelIdc = config.levelIdc; 38803 track.profileCompatibility = config.profileCompatibility; 38804 h264Frame.endNalUnit(); 38805 } else if (currentNal.nalUnitType === 'pic_parameter_set_rbsp') { 38806 track.newMetadata = true; 38807 track.pps = [currentNal.data]; 38808 h264Frame.endNalUnit(); 38809 } else if (currentNal.nalUnitType === 'access_unit_delimiter_rbsp') { 38810 if (h264Frame) { 38811 this.finishFrame(tags, h264Frame); 38812 } 38813 h264Frame = new FlvTag(FlvTag.VIDEO_TAG); 38814 h264Frame.pts = currentNal.pts; 38815 h264Frame.dts = currentNal.dts; 38816 } else { 38817 if (currentNal.nalUnitType === 'slice_layer_without_partitioning_rbsp_idr') { 38818 // the current sample is a key frame 38819 h264Frame.keyFrame = true; 38820 } 38821 h264Frame.endNalUnit(); 38822 } 38823 h264Frame.startNalUnit(); 38824 h264Frame.writeBytes(currentNal.data); 38825 } 38826 if (h264Frame) { 38827 this.finishFrame(tags, h264Frame); 38828 } 38829 38830 this.trigger('data', {track: track, tags: tags.list}); 38831 38832 // Continue with the flush process now 38833 this.trigger('done', 'VideoSegmentStream'); 38834 }; 38835}; 38836 38837VideoSegmentStream.prototype = new Stream(); 38838 38839/** 38840 * An object that incrementally transmuxes MPEG2 Trasport Stream 38841 * chunks into an FLV. 38842 */ 38843Transmuxer = function(options) { 38844 var 38845 self = this, 38846 38847 packetStream, parseStream, elementaryStream, 38848 videoTimestampRolloverStream, audioTimestampRolloverStream, 38849 timedMetadataTimestampRolloverStream, 38850 adtsStream, h264Stream, 38851 videoSegmentStream, audioSegmentStream, captionStream, 38852 coalesceStream; 38853 38854 Transmuxer.prototype.init.call(this); 38855 38856 options = options || {}; 38857 38858 // expose the metadata stream 38859 this.metadataStream = new m2ts.MetadataStream(); 38860 38861 options.metadataStream = this.metadataStream; 38862 38863 // set up the parsing pipeline 38864 packetStream = new m2ts.TransportPacketStream(); 38865 parseStream = new m2ts.TransportParseStream(); 38866 elementaryStream = new m2ts.ElementaryStream(); 38867 videoTimestampRolloverStream = new m2ts.TimestampRolloverStream('video'); 38868 audioTimestampRolloverStream = new m2ts.TimestampRolloverStream('audio'); 38869 timedMetadataTimestampRolloverStream = new m2ts.TimestampRolloverStream('timed-metadata'); 38870 38871 adtsStream = new AdtsStream(); 38872 h264Stream = new H264Stream(); 38873 coalesceStream = new CoalesceStream(options); 38874 38875 // disassemble MPEG2-TS packets into elementary streams 38876 packetStream 38877 .pipe(parseStream) 38878 .pipe(elementaryStream); 38879 38880 // !!THIS ORDER IS IMPORTANT!! 38881 // demux the streams 38882 elementaryStream 38883 .pipe(videoTimestampRolloverStream) 38884 .pipe(h264Stream); 38885 elementaryStream 38886 .pipe(audioTimestampRolloverStream) 38887 .pipe(adtsStream); 38888 38889 elementaryStream 38890 .pipe(timedMetadataTimestampRolloverStream) 38891 .pipe(this.metadataStream) 38892 .pipe(coalesceStream); 38893 // if CEA-708 parsing is available, hook up a caption stream 38894 captionStream = new m2ts.CaptionStream(); 38895 h264Stream.pipe(captionStream) 38896 .pipe(coalesceStream); 38897 38898 // hook up the segment streams once track metadata is delivered 38899 elementaryStream.on('data', function(data) { 38900 var i, videoTrack, audioTrack; 38901 38902 if (data.type === 'metadata') { 38903 i = data.tracks.length; 38904 38905 // scan the tracks listed in the metadata 38906 while (i--) { 38907 if (data.tracks[i].type === 'video') { 38908 videoTrack = data.tracks[i]; 38909 } else if (data.tracks[i].type === 'audio') { 38910 audioTrack = data.tracks[i]; 38911 } 38912 } 38913 38914 // hook up the video segment stream to the first track with h264 data 38915 if (videoTrack && !videoSegmentStream) { 38916 coalesceStream.numberOfTracks++; 38917 videoSegmentStream = new VideoSegmentStream(videoTrack); 38918 38919 // Set up the final part of the video pipeline 38920 h264Stream 38921 .pipe(videoSegmentStream) 38922 .pipe(coalesceStream); 38923 } 38924 38925 if (audioTrack && !audioSegmentStream) { 38926 // hook up the audio segment stream to the first track with aac data 38927 coalesceStream.numberOfTracks++; 38928 audioSegmentStream = new AudioSegmentStream(audioTrack); 38929 38930 // Set up the final part of the audio pipeline 38931 adtsStream 38932 .pipe(audioSegmentStream) 38933 .pipe(coalesceStream); 38934 38935 if (videoSegmentStream) { 38936 videoSegmentStream.on('keyframe', audioSegmentStream.onVideoKeyFrame); 38937 } 38938 } 38939 } 38940 }); 38941 38942 // feed incoming data to the front of the parsing pipeline 38943 this.push = function(data) { 38944 packetStream.push(data); 38945 }; 38946 38947 // flush any buffered data 38948 this.flush = function() { 38949 // Start at the top of the pipeline and flush all pending work 38950 packetStream.flush(); 38951 }; 38952 38953 // Caption data has to be reset when seeking outside buffered range 38954 this.resetCaptions = function() { 38955 captionStream.reset(); 38956 }; 38957 38958 // Re-emit any data coming from the coalesce stream to the outside world 38959 coalesceStream.on('data', function(event) { 38960 self.trigger('data', event); 38961 }); 38962 38963 // Let the consumer know we have finished flushing the entire pipeline 38964 coalesceStream.on('done', function() { 38965 self.trigger('done'); 38966 }); 38967}; 38968Transmuxer.prototype = new Stream(); 38969 38970// forward compatibility 38971module.exports = Transmuxer; 38972 38973},{"../codecs/adts.js":40,"../codecs/h264":41,"../m2ts/m2ts.js":50,"../utils/stream.js":62,"./coalesce-stream.js":43,"./flv-tag.js":45,"./tag-list.js":47}],49:[function(require,module,exports){ 38974/** 38975 * mux.js 38976 * 38977 * Copyright (c) 2015 Brightcove 38978 * All rights reserved. 38979 * 38980 * Reads in-band caption information from a video elementary 38981 * stream. Captions must follow the CEA-708 standard for injection 38982 * into an MPEG-2 transport streams.
vendor: 7,004 bytes, lines 38983-39231
38983 * @see https://en.wikipedia.org/wiki/CEA-708 38984 * @see https://www.gpo.gov/fdsys/pkg/CFR-2007-title47-vol1/pdf/CFR-2007-title47-vol1-sec15-119.pdf 38985 */ 38986 38987'use strict'; 38988 38989// ----------------- 38990// Link To Transport 38991// ----------------- 38992 38993// Supplemental enhancement information (SEI) NAL units have a 38994// payload type field to indicate how they are to be 38995// interpreted. CEAS-708 caption content is always transmitted with 38996// payload type 0x04. 38997var USER_DATA_REGISTERED_ITU_T_T35 = 4, 38998 RBSP_TRAILING_BITS = 128, 38999 Stream = require('../utils/stream'); 39000 39001/** 39002 * Parse a supplemental enhancement information (SEI) NAL unit. 39003 * Stops parsing once a message of type ITU T T35 has been found. 39004 * 39005 * @param bytes {Uint8Array} the bytes of a SEI NAL unit 39006 * @return {object} the parsed SEI payload 39007 * @see Rec. ITU-T H.264, 7.3.2.3.1 39008 */ 39009var parseSei = function(bytes) { 39010 var 39011 i = 0, 39012 result = { 39013 payloadType: -1, 39014 payloadSize: 0 39015 }, 39016 payloadType = 0, 39017 payloadSize = 0; 39018 39019 // go through the sei_rbsp parsing each each individual sei_message 39020 while (i < bytes.byteLength) { 39021 // stop once we have hit the end of the sei_rbsp 39022 if (bytes[i] === RBSP_TRAILING_BITS) { 39023 break; 39024 } 39025 39026 // Parse payload type 39027 while (bytes[i] === 0xFF) { 39028 payloadType += 255; 39029 i++; 39030 } 39031 payloadType += bytes[i++]; 39032 39033 // Parse payload size 39034 while (bytes[i] === 0xFF) { 39035 payloadSize += 255; 39036 i++; 39037 } 39038 payloadSize += bytes[i++]; 39039 39040 // this sei_message is a 608/708 caption so save it and break 39041 // there can only ever be one caption message in a frame's sei 39042 if (!result.payload && payloadType === USER_DATA_REGISTERED_ITU_T_T35) { 39043 result.payloadType = payloadType; 39044 result.payloadSize = payloadSize; 39045 result.payload = bytes.subarray(i, i + payloadSize); 39046 break; 39047 } 39048 39049 // skip the payload and parse the next message 39050 i += payloadSize; 39051 payloadType = 0; 39052 payloadSize = 0; 39053 } 39054 39055 return result; 39056}; 39057 39058// see ANSI/SCTE 128-1 (2013), section 8.1 39059var parseUserData = function(sei) { 39060 // itu_t_t35_contry_code must be 181 (United States) for 39061 // captions 39062 if (sei.payload[0] !== 181) { 39063 return null; 39064 } 39065 39066 // itu_t_t35_provider_code should be 49 (ATSC) for captions 39067 if (((sei.payload[1] << 8) | sei.payload[2]) !== 49) { 39068 return null; 39069 } 39070 39071 // the user_identifier should be "GA94" to indicate ATSC1 data 39072 if (String.fromCharCode(sei.payload[3], 39073 sei.payload[4], 39074 sei.payload[5], 39075 sei.payload[6]) !== 'GA94') { 39076 return null; 39077 } 39078 39079 // finally, user_data_type_code should be 0x03 for caption data 39080 if (sei.payload[7] !== 0x03) { 39081 return null; 39082 } 39083 39084 // return the user_data_type_structure and strip the trailing 39085 // marker bits 39086 return sei.payload.subarray(8, sei.payload.length - 1); 39087}; 39088 39089// see CEA-708-D, section 4.4 39090var parseCaptionPackets = function(pts, userData) { 39091 var results = [], i, count, offset, data; 39092 39093 // if this is just filler, return immediately 39094 if (!(userData[0] & 0x40)) { 39095 return results; 39096 } 39097 39098 // parse out the cc_data_1 and cc_data_2 fields 39099 count = userData[0] & 0x1f; 39100 for (i = 0; i < count; i++) { 39101 offset = i * 3; 39102 data = { 39103 type: userData[offset + 2] & 0x03, 39104 pts: pts 39105 }; 39106 39107 // capture cc data when cc_valid is 1 39108 if (userData[offset + 2] & 0x04) { 39109 data.ccData = (userData[offset + 3] << 8) | userData[offset + 4]; 39110 results.push(data); 39111 } 39112 } 39113 return results; 39114}; 39115 39116var packetDropper = { 39117 push: function() {} 39118}; 39119 39120var CaptionStream = function() { 39121 39122 CaptionStream.prototype.init.call(this); 39123 39124 this.captionPackets_ = []; 39125 39126 this.ccStreams_ = [ 39127 new Cea608Stream(0, 0), // eslint-disable-line no-use-before-define 39128 new Cea608Stream(0, 1), // eslint-disable-line no-use-before-define 39129 new Cea608Stream(1, 0), // eslint-disable-line no-use-before-define 39130 new Cea608Stream(1, 1) // eslint-disable-line no-use-before-define 39131 ]; 39132 39133 this.reset(); 39134 39135 // forward data and done events from CCs to this CaptionStream 39136 this.ccStreams_.forEach(function(cc) { 39137 cc.on('data', this.trigger.bind(this, 'data')); 39138 cc.on('done', this.trigger.bind(this, 'done')); 39139 }, this); 39140 39141}; 39142 39143CaptionStream.prototype = new Stream(); 39144CaptionStream.prototype.push = function(event) { 39145 var sei, userData; 39146 39147 // only examine SEI NALs 39148 if (event.nalUnitType !== 'sei_rbsp') { 39149 return; 39150 } 39151 39152 // parse the sei 39153 sei = parseSei(event.escapedRBSP); 39154 39155 // ignore everything but user_data_registered_itu_t_t35 39156 if (sei.payloadType !== USER_DATA_REGISTERED_ITU_T_T35) { 39157 return; 39158 } 39159 39160 // parse out the user data payload 39161 userData = parseUserData(sei); 39162 39163 // ignore unrecognized userData 39164 if (!userData) { 39165 return; 39166 } 39167 39168 // Sometimes, the same segment # will be downloaded twice. To stop the 39169 // caption data from being processed twice, we track the latest dts we've 39170 // received and ignore everything with a dts before that. However, since 39171 // data for a specific dts can be split across 2 packets on either side of 39172 // a segment boundary, we need to make sure we *don't* ignore the second 39173 // dts packet we receive that has dts === this.latestDts_. And thus, the 39174 // ignoreNextEqualDts_ flag was born. 39175 if (event.dts < this.latestDts_) { 39176 // We've started getting older data, so set the flag. 39177 this.ignoreNextEqualDts_ = true; 39178 return; 39179 } else if ((event.dts === this.latestDts_) && (this.ignoreNextEqualDts_)) { 39180 // We've received the last duplicate packet, time to start processing again 39181 this.ignoreNextEqualDts_ = false; 39182 return; 39183 } 39184 39185 // parse out CC data packets and save them for later 39186 this.captionPackets_ = this.captionPackets_.concat(parseCaptionPackets(event.pts, userData)); 39187 this.latestDts_ = event.dts; 39188}; 39189 39190CaptionStream.prototype.flush = function() { 39191 // make sure we actually parsed captions before proceeding 39192 if (!this.captionPackets_.length) { 39193 this.ccStreams_.forEach(function(cc) { 39194 cc.flush(); 39195 }, this); 39196 return; 39197 } 39198 39199 // In Chrome, the Array#sort function is not stable so add a 39200 // presortIndex that we can use to ensure we get a stable-sort 39201 this.captionPackets_.forEach(function(elem, idx) { 39202 elem.presortIndex = idx; 39203 }); 39204 39205 // sort caption byte-pairs based on their PTS values 39206 this.captionPackets_.sort(function(a, b) { 39207 if (a.pts === b.pts) { 39208 return a.presortIndex - b.presortIndex; 39209 } 39210 return a.pts - b.pts; 39211 }); 39212 39213 this.captionPackets_.forEach(function(packet) { 39214 if (packet.type < 2) { 39215 // Dispatch packet to the right Cea608Stream 39216 this.dispatchCea608Packet(packet); 39217 } 39218 // this is where an 'else' would go for a dispatching packets 39219 // to a theoretical Cea708Stream that handles SERVICEn data 39220 }, this); 39221 39222 this.captionPackets_.length = 0; 39223 this.ccStreams_.forEach(function(cc) { 39224 cc.flush(); 39225 }, this); 39226 return; 39227}; 39228 39229CaptionStream.prototype.reset = function() { 39230 this.latestDts_ = null; 39231 this.ignoreNextEqualDts_ = false;
39232 this.activeCea608Channel_ = null; 39233 // Since we don't know which channel is active until we get a control 39234 // code that sets it, we start off with CEA608 handlers that just drop 39235 // all the packets. 39236 this.activeCea608Streams_ = [ 39237 packetDropper, 39238 packetDropper 39239 ]; 39240 this.ccStreams_.forEach(function(ccStream) { 39241 ccStream.reset(); 39242 }); 39243}; 39244 39245CaptionStream.prototype.dispatchCea608Packet = function(packet) { 39246 if (this.setsChannel1Active(packet) && this.activeCea608Channel_ !== 1) { 39247 this.activeCea608Channel_ = 1; 39248 this.activeCea608Streams_ = [this.ccStreams_[0], this.ccStreams_[2]]; // CC1, CC3 39249 } else if (this.setsChannel2Active(packet) && this.activeCea608Channel_ !== 2) { 39250 this.activeCea608Channel_ = 2; 39251 this.activeCea608Streams_ = [this.ccStreams_[1], this.ccStreams_[3]]; // CC2, CC4 39252 } 39253 // If we haven't set the active streams yet, this next call just returns 39254 // immediately. 39255 this.activeCea608Streams_[packet.type].push(packet); 39256}; 39257 39258CaptionStream.prototype.setsChannel1Active = function(packet) { 39259 return ((packet.ccData & 0x7800) === 0x1000); 39260}; 39261CaptionStream.prototype.setsChannel2Active = function(packet) { 39262 return ((packet.ccData & 0x7800) === 0x1800); 39263}; 39264 39265// ---------------------- 39266// Session to Application 39267// ---------------------- 39268 39269var CHARACTER_TRANSLATION = { 39270 0x2a: 0xe1, // á 39271 0x5c: 0xe9, // é 39272 0x5e: 0xed, // à 39273 0x5f: 0xf3, // ó 39274 0x60: 0xfa, // ú 39275 0x7b: 0xe7, // ç 39276 0x7c: 0xf7, // ÷ 39277 0x7d: 0xd1, // à 39278 0x7e: 0xf1, // ñ 39279 0x7f: 0x2588, // â 39280 0x0130: 0xae, // ® 39281 0x0131: 0xb0, // ° 39282 0x0132: 0xbd, // ½ 39283 0x0133: 0xbf, // ¿ 39284 0x0134: 0x2122, // ⢠39285 0x0135: 0xa2, // ¢ 39286 0x0136: 0xa3, // £ 39287 0x0137: 0x266a, // ⪠39288 0x0138: 0xe0, // à 39289 0x0139: 0xa0, // 39290 0x013a: 0xe8, // è 39291 0x013b: 0xe2, // â 39292 0x013c: 0xea, // ê 39293 0x013d: 0xee, // î 39294 0x013e: 0xf4, // ô 39295 0x013f: 0xfb, // û 39296 0x0220: 0xc1, // à 39297 0x0221: 0xc9, // à 39298 0x0222: 0xd3, // à 39299 0x0223: 0xda, // à 39300 0x0224: 0xdc, // à 39301 0x0225: 0xfc, // ü 39302 0x0226: 0x2018, // â 39303 0x0227: 0xa1, // ¡ 39304 0x0228: 0x2a, // * 39305 0x0229: 0x27, // ' 39306 0x022a: 0x2014, // â 39307 0x022b: 0xa9, // © 39308 0x022c: 0x2120, // â 39309 0x022d: 0x2022, // ⢠39310 0x022e: 0x201c, // â 39311 0x022f: 0x201d, // â 39312 0x0230: 0xc0, // à 39313 0x0231: 0xc2, // à 39314 0x0232: 0xc7, // à 39315 0x0233: 0xc8, // à 39316 0x0234: 0xca, // à 39317 0x0235: 0xcb, // à 39318 0x0236: 0xeb, // ë 39319 0x0237: 0xce, // à 39320 0x0238: 0xcf, // à 39321 0x0239: 0xef, // ï 39322 0x023a: 0xd4, // à 39323 0x023b: 0xd9, // à 39324 0x023c: 0xf9, // ù 39325 0x023d: 0xdb, // à 39326 0x023e: 0xab, // « 39327 0x023f: 0xbb, // » 39328 0x0320: 0xc3, // à 39329 0x0321: 0xe3, // ã 39330 0x0322: 0xcd, // à 39331 0x0323: 0xcc, // à 39332 0x0324: 0xec, // ì 39333 0x0325: 0xd2, // à 39334 0x0326: 0xf2, // ò 39335 0x0327: 0xd5, // à 39336 0x0328: 0xf5, // õ 39337 0x0329: 0x7b, // { 39338 0x032a: 0x7d, // } 39339 0x032b: 0x5c, // \ 39340 0x032c: 0x5e, // ^ 39341 0x032d: 0x5f, // _ 39342 0x032e: 0x7c, // | 39343 0x032f: 0x7e, // ~ 39344 0x0330: 0xc4, // à 39345 0x0331: 0xe4, // ä 39346 0x0332: 0xd6, // à 39347 0x0333: 0xf6, // ö 39348 0x0334: 0xdf, // à 39349 0x0335: 0xa5, // Â¥ 39350 0x0336: 0xa4, // ¤ 39351 0x0337: 0x2502, // â 39352 0x0338: 0xc5, // à 39353 0x0339: 0xe5, // Ã¥ 39354 0x033a: 0xd8, // à 39355 0x033b: 0xf8, // ø 39356 0x033c: 0x250c, // â 39357 0x033d: 0x2510, // â 39358 0x033e: 0x2514, // â 39359 0x033f: 0x2518 // â 39360}; 39361 39362var getCharFromCode = function(code) { 39363 if (code === null) { 39364 return ''; 39365 } 39366 code = CHARACTER_TRANSLATION[code] || code; 39367 return String.fromCharCode(code); 39368}; 39369 39370// the index of the last row in a CEA-608 display buffer 39371var BOTTOM_ROW = 14; 39372 39373// This array is used for mapping PACs -> row #, since there's no way of 39374// getting it through bit logic. 39375var ROWS = [0x1100, 0x1120, 0x1200, 0x1220, 0x1500, 0x1520, 0x1600, 0x1620, 39376 0x1700, 0x1720, 0x1000, 0x1300, 0x1320, 0x1400, 0x1420]; 39377 39378// CEA-608 captions are rendered onto a 34x15 matrix of character 39379// cells. The "bottom" row is the last element in the outer array. 39380var createDisplayBuffer = function() { 39381 var result = [], i = BOTTOM_ROW + 1; 39382 while (i--) { 39383 result.push(''); 39384 } 39385 return result; 39386}; 39387 39388var Cea608Stream = function(field, dataChannel) { 39389 Cea608Stream.prototype.init.call(this); 39390 39391 this.field_ = field || 0; 39392 this.dataChannel_ = dataChannel || 0; 39393 39394 this.name_ = 'CC' + (((this.field_ << 1) | this.dataChannel_) + 1); 39395 39396 this.setConstants(); 39397 this.reset(); 39398 39399 this.push = function(packet) { 39400 var data, swap, char0, char1, text; 39401 // remove the parity bits 39402 data = packet.ccData & 0x7f7f; 39403 39404 // ignore duplicate control codes; the spec demands they're sent twice 39405 if (data === this.lastControlCode_) { 39406 this.lastControlCode_ = null; 39407 return; 39408 } 39409 39410 // Store control codes 39411 if ((data & 0xf000) === 0x1000) { 39412 this.lastControlCode_ = data; 39413 } else if (data !== this.PADDING_) { 39414 this.lastControlCode_ = null; 39415 } 39416 39417 char0 = data >>> 8; 39418 char1 = data & 0xff;
vendor: 4,062 bytes, lines 39419-39525
39419 39420 if (data === this.PADDING_) { 39421 return; 39422 39423 } else if (data === this.RESUME_CAPTION_LOADING_) { 39424 this.mode_ = 'popOn'; 39425 39426 } else if (data === this.END_OF_CAPTION_) { 39427 this.clearFormatting(packet.pts); 39428 // if a caption was being displayed, it's gone now 39429 this.flushDisplayed(packet.pts); 39430 39431 // flip memory 39432 swap = this.displayed_; 39433 this.displayed_ = this.nonDisplayed_; 39434 this.nonDisplayed_ = swap; 39435 39436 // start measuring the time to display the caption 39437 this.startPts_ = packet.pts; 39438 39439 } else if (data === this.ROLL_UP_2_ROWS_) { 39440 this.topRow_ = BOTTOM_ROW - 1; 39441 this.mode_ = 'rollUp'; 39442 } else if (data === this.ROLL_UP_3_ROWS_) { 39443 this.topRow_ = BOTTOM_ROW - 2; 39444 this.mode_ = 'rollUp'; 39445 } else if (data === this.ROLL_UP_4_ROWS_) { 39446 this.topRow_ = BOTTOM_ROW - 3; 39447 this.mode_ = 'rollUp'; 39448 } else if (data === this.CARRIAGE_RETURN_) { 39449 this.clearFormatting(packet.pts); 39450 this.flushDisplayed(packet.pts); 39451 this.shiftRowsUp_(); 39452 this.startPts_ = packet.pts; 39453 39454 } else if (data === this.BACKSPACE_) { 39455 if (this.mode_ === 'popOn') { 39456 this.nonDisplayed_[BOTTOM_ROW] = this.nonDisplayed_[BOTTOM_ROW].slice(0, -1); 39457 } else { 39458 this.displayed_[BOTTOM_ROW] = this.displayed_[BOTTOM_ROW].slice(0, -1); 39459 } 39460 } else if (data === this.ERASE_DISPLAYED_MEMORY_) { 39461 this.flushDisplayed(packet.pts); 39462 this.displayed_ = createDisplayBuffer(); 39463 } else if (data === this.ERASE_NON_DISPLAYED_MEMORY_) { 39464 this.nonDisplayed_ = createDisplayBuffer(); 39465 39466 } else if (data === this.RESUME_DIRECT_CAPTIONING_) { 39467 this.mode_ = 'paintOn'; 39468 39469 // Append special characters to caption text 39470 } else if (this.isSpecialCharacter(char0, char1)) { 39471 // Bitmask char0 so that we can apply character transformations 39472 // regardless of field and data channel. 39473 // Then byte-shift to the left and OR with char1 so we can pass the 39474 // entire character code to `getCharFromCode`. 39475 char0 = (char0 & 0x03) << 8; 39476 text = getCharFromCode(char0 | char1); 39477 this[this.mode_](packet.pts, text); 39478 this.column_++; 39479 39480 // Append extended characters to caption text 39481 } else if (this.isExtCharacter(char0, char1)) { 39482 // Extended characters always follow their "non-extended" equivalents. 39483 // IE if a "è" is desired, you'll always receive "eè"; non-compliant 39484 // decoders are supposed to drop the "è", while compliant decoders 39485 // backspace the "e" and insert "è". 39486 39487 // Delete the previous character 39488 if (this.mode_ === 'popOn') { 39489 this.nonDisplayed_[this.row_] = this.nonDisplayed_[this.row_].slice(0, -1); 39490 } else { 39491 this.displayed_[BOTTOM_ROW] = this.displayed_[BOTTOM_ROW].slice(0, -1); 39492 } 39493 39494 // Bitmask char0 so that we can apply character transformations 39495 // regardless of field and data channel. 39496 // Then byte-shift to the left and OR with char1 so we can pass the 39497 // entire character code to `getCharFromCode`. 39498 char0 = (char0 & 0x03) << 8; 39499 text = getCharFromCode(char0 | char1); 39500 this[this.mode_](packet.pts, text); 39501 this.column_++; 39502 39503 // Process mid-row codes 39504 } else if (this.isMidRowCode(char0, char1)) { 39505 // Attributes are not additive, so clear all formatting 39506 this.clearFormatting(packet.pts); 39507 39508 // According to the standard, mid-row codes 39509 // should be replaced with spaces, so add one now 39510 this[this.mode_](packet.pts, ' '); 39511 this.column_++; 39512 39513 if ((char1 & 0xe) === 0xe) { 39514 this.addFormatting(packet.pts, ['i']); 39515 } 39516 39517 if ((char1 & 0x1) === 0x1) { 39518 this.addFormatting(packet.pts, ['u']); 39519 } 39520 39521 // Detect offset control codes and adjust cursor 39522 } else if (this.isOffsetControlCode(char0, char1)) { 39523 // Cursor position is set by indent PAC (see below) in 4-column 39524 // increments, with an additional offset code of 1-3 to reach any 39525 // of the 32 columns specified by CEA-608. So all we need to do
vendor: 24,821 bytes, lines 39526-40273
39526 // here is increment the column cursor by the given offset. 39527 this.column_ += (char1 & 0x03); 39528 39529 // Detect PACs (Preamble Address Codes) 39530 } else if (this.isPAC(char0, char1)) { 39531 39532 // There's no logic for PAC -> row mapping, so we have to just 39533 // find the row code in an array and use its index :( 39534 var row = ROWS.indexOf(data & 0x1f20); 39535 39536 if (row !== this.row_) { 39537 // formatting is only persistent for current row 39538 this.clearFormatting(packet.pts); 39539 this.row_ = row; 39540 } 39541 // All PACs can apply underline, so detect and apply 39542 // (All odd-numbered second bytes set underline) 39543 if ((char1 & 0x1) && (this.formatting_.indexOf('u') === -1)) { 39544 this.addFormatting(packet.pts, ['u']); 39545 } 39546 39547 if ((data & 0x10) === 0x10) { 39548 // We've got an indent level code. Each successive even number 39549 // increments the column cursor by 4, so we can get the desired 39550 // column position by bit-shifting to the right (to get n/2) 39551 // and multiplying by 4. 39552 this.column_ = ((data & 0xe) >> 1) * 4; 39553 } 39554 39555 if (this.isColorPAC(char1)) { 39556 // it's a color code, though we only support white, which 39557 // can be either normal or italicized. white italics can be 39558 // either 0x4e or 0x6e depending on the row, so we just 39559 // bitwise-and with 0xe to see if italics should be turned on 39560 if ((char1 & 0xe) === 0xe) { 39561 this.addFormatting(packet.pts, ['i']); 39562 } 39563 } 39564 39565 // We have a normal character in char0, and possibly one in char1 39566 } else if (this.isNormalChar(char0)) { 39567 if (char1 === 0x00) { 39568 char1 = null; 39569 } 39570 text = getCharFromCode(char0); 39571 text += getCharFromCode(char1); 39572 this[this.mode_](packet.pts, text); 39573 this.column_ += text.length; 39574 39575 } // finish data processing 39576 39577 }; 39578}; 39579Cea608Stream.prototype = new Stream(); 39580// Trigger a cue point that captures the current state of the 39581// display buffer 39582Cea608Stream.prototype.flushDisplayed = function(pts) { 39583 var content = this.displayed_ 39584 // remove spaces from the start and end of the string 39585 .map(function(row) { 39586 return row.trim(); 39587 }) 39588 // combine all text rows to display in one cue 39589 .join('\n') 39590 // and remove blank rows from the start and end, but not the middle 39591 .replace(/^\n+|\n+$/g, ''); 39592 39593 if (content.length) { 39594 this.trigger('data', { 39595 startPts: this.startPts_, 39596 endPts: pts, 39597 text: content, 39598 stream: this.name_ 39599 }); 39600 } 39601}; 39602 39603/** 39604 * Zero out the data, used for startup and on seek 39605 */ 39606Cea608Stream.prototype.reset = function() { 39607 this.mode_ = 'popOn'; 39608 // When in roll-up mode, the index of the last row that will 39609 // actually display captions. If a caption is shifted to a row 39610 // with a lower index than this, it is cleared from the display 39611 // buffer 39612 this.topRow_ = 0; 39613 this.startPts_ = 0; 39614 this.displayed_ = createDisplayBuffer(); 39615 this.nonDisplayed_ = createDisplayBuffer(); 39616 this.lastControlCode_ = null; 39617 39618 // Track row and column for proper line-breaking and spacing 39619 this.column_ = 0; 39620 this.row_ = BOTTOM_ROW; 39621 39622 // This variable holds currently-applied formatting 39623 this.formatting_ = []; 39624}; 39625 39626/** 39627 * Sets up control code and related constants for this instance 39628 */ 39629Cea608Stream.prototype.setConstants = function() { 39630 // The following attributes have these uses: 39631 // ext_ : char0 for mid-row codes, and the base for extended 39632 // chars (ext_+0, ext_+1, and ext_+2 are char0s for 39633 // extended codes) 39634 // control_: char0 for control codes, except byte-shifted to the 39635 // left so that we can do this.control_ | CONTROL_CODE 39636 // offset_: char0 for tab offset codes 39637 // 39638 // It's also worth noting that control codes, and _only_ control codes, 39639 // differ between field 1 and field2. Field 2 control codes are always 39640 // their field 1 value plus 1. That's why there's the "| field" on the 39641 // control value. 39642 if (this.dataChannel_ === 0) { 39643 this.BASE_ = 0x10; 39644 this.EXT_ = 0x11; 39645 this.CONTROL_ = (0x14 | this.field_) << 8; 39646 this.OFFSET_ = 0x17; 39647 } else if (this.dataChannel_ === 1) { 39648 this.BASE_ = 0x18; 39649 this.EXT_ = 0x19; 39650 this.CONTROL_ = (0x1c | this.field_) << 8; 39651 this.OFFSET_ = 0x1f; 39652 } 39653 39654 // Constants for the LSByte command codes recognized by Cea608Stream. This 39655 // list is not exhaustive. For a more comprehensive listing and semantics see 39656 // http://www.gpo.gov/fdsys/pkg/CFR-2010-title47-vol1/pdf/CFR-2010-title47-vol1-sec15-119.pdf 39657 // Padding 39658 this.PADDING_ = 0x0000; 39659 // Pop-on Mode 39660 this.RESUME_CAPTION_LOADING_ = this.CONTROL_ | 0x20; 39661 this.END_OF_CAPTION_ = this.CONTROL_ | 0x2f; 39662 // Roll-up Mode 39663 this.ROLL_UP_2_ROWS_ = this.CONTROL_ | 0x25; 39664 this.ROLL_UP_3_ROWS_ = this.CONTROL_ | 0x26; 39665 this.ROLL_UP_4_ROWS_ = this.CONTROL_ | 0x27; 39666 this.CARRIAGE_RETURN_ = this.CONTROL_ | 0x2d; 39667 // paint-on mode (not supported) 39668 this.RESUME_DIRECT_CAPTIONING_ = this.CONTROL_ | 0x29; 39669 // Erasure 39670 this.BACKSPACE_ = this.CONTROL_ | 0x21; 39671 this.ERASE_DISPLAYED_MEMORY_ = this.CONTROL_ | 0x2c; 39672 this.ERASE_NON_DISPLAYED_MEMORY_ = this.CONTROL_ | 0x2e; 39673}; 39674 39675/** 39676 * Detects if the 2-byte packet data is a special character 39677 * 39678 * Special characters have a second byte in the range 0x30 to 0x3f, 39679 * with the first byte being 0x11 (for data channel 1) or 0x19 (for 39680 * data channel 2). 39681 * 39682 * @param {Integer} char0 The first byte 39683 * @param {Integer} char1 The second byte 39684 * @return {Boolean} Whether the 2 bytes are an special character 39685 */ 39686Cea608Stream.prototype.isSpecialCharacter = function(char0, char1) { 39687 return (char0 === this.EXT_ && char1 >= 0x30 && char1 <= 0x3f); 39688}; 39689 39690/** 39691 * Detects if the 2-byte packet data is an extended character 39692 * 39693 * Extended characters have a second byte in the range 0x20 to 0x3f, 39694 * with the first byte being 0x12 or 0x13 (for data channel 1) or 39695 * 0x1a or 0x1b (for data channel 2). 39696 * 39697 * @param {Integer} char0 The first byte 39698 * @param {Integer} char1 The second byte 39699 * @return {Boolean} Whether the 2 bytes are an extended character 39700 */ 39701Cea608Stream.prototype.isExtCharacter = function(char0, char1) { 39702 return ((char0 === (this.EXT_ + 1) || char0 === (this.EXT_ + 2)) && 39703 (char1 >= 0x20 && char1 <= 0x3f)); 39704}; 39705 39706/** 39707 * Detects if the 2-byte packet is a mid-row code 39708 * 39709 * Mid-row codes have a second byte in the range 0x20 to 0x2f, with 39710 * the first byte being 0x11 (for data channel 1) or 0x19 (for data 39711 * channel 2). 39712 * 39713 * @param {Integer} char0 The first byte 39714 * @param {Integer} char1 The second byte 39715 * @return {Boolean} Whether the 2 bytes are a mid-row code 39716 */ 39717Cea608Stream.prototype.isMidRowCode = function(char0, char1) { 39718 return (char0 === this.EXT_ && (char1 >= 0x20 && char1 <= 0x2f)); 39719}; 39720 39721/** 39722 * Detects if the 2-byte packet is an offset control code 39723 * 39724 * Offset control codes have a second byte in the range 0x21 to 0x23, 39725 * with the first byte being 0x17 (for data channel 1) or 0x1f (for 39726 * data channel 2). 39727 * 39728 * @param {Integer} char0 The first byte 39729 * @param {Integer} char1 The second byte 39730 * @return {Boolean} Whether the 2 bytes are an offset control code 39731 */ 39732Cea608Stream.prototype.isOffsetControlCode = function(char0, char1) { 39733 return (char0 === this.OFFSET_ && (char1 >= 0x21 && char1 <= 0x23)); 39734}; 39735 39736/** 39737 * Detects if the 2-byte packet is a Preamble Address Code 39738 * 39739 * PACs have a first byte in the range 0x10 to 0x17 (for data channel 1) 39740 * or 0x18 to 0x1f (for data channel 2), with the second byte in the 39741 * range 0x40 to 0x7f. 39742 * 39743 * @param {Integer} char0 The first byte 39744 * @param {Integer} char1 The second byte 39745 * @return {Boolean} Whether the 2 bytes are a PAC 39746 */ 39747Cea608Stream.prototype.isPAC = function(char0, char1) { 39748 return (char0 >= this.BASE_ && char0 < (this.BASE_ + 8) && 39749 (char1 >= 0x40 && char1 <= 0x7f)); 39750}; 39751 39752/** 39753 * Detects if a packet's second byte is in the range of a PAC color code 39754 * 39755 * PAC color codes have the second byte be in the range 0x40 to 0x4f, or 39756 * 0x60 to 0x6f. 39757 * 39758 * @param {Integer} char1 The second byte 39759 * @return {Boolean} Whether the byte is a color PAC 39760 */ 39761Cea608Stream.prototype.isColorPAC = function(char1) { 39762 return ((char1 >= 0x40 && char1 <= 0x4f) || (char1 >= 0x60 && char1 <= 0x7f)); 39763}; 39764 39765/** 39766 * Detects if a single byte is in the range of a normal character 39767 * 39768 * Normal text bytes are in the range 0x20 to 0x7f. 39769 * 39770 * @param {Integer} char The byte 39771 * @return {Boolean} Whether the byte is a normal character 39772 */ 39773Cea608Stream.prototype.isNormalChar = function(char) { 39774 return (char >= 0x20 && char <= 0x7f); 39775}; 39776 39777// Adds the opening HTML tag for the passed character to the caption text, 39778// and keeps track of it for later closing 39779Cea608Stream.prototype.addFormatting = function(pts, format) { 39780 this.formatting_ = this.formatting_.concat(format); 39781 var text = format.reduce(function(text, format) { 39782 return text + '<' + format + '>'; 39783 }, ''); 39784 this[this.mode_](pts, text); 39785}; 39786 39787// Adds HTML closing tags for current formatting to caption text and 39788// clears remembered formatting 39789Cea608Stream.prototype.clearFormatting = function(pts) { 39790 if (!this.formatting_.length) { 39791 return; 39792 } 39793 var text = this.formatting_.reverse().reduce(function(text, format) { 39794 return text + '</' + format + '>'; 39795 }, ''); 39796 this.formatting_ = []; 39797 this[this.mode_](pts, text); 39798}; 39799 39800// Mode Implementations 39801Cea608Stream.prototype.popOn = function(pts, text) { 39802 var baseRow = this.nonDisplayed_[this.row_]; 39803 39804 // buffer characters 39805 baseRow += text; 39806 this.nonDisplayed_[this.row_] = baseRow; 39807}; 39808 39809Cea608Stream.prototype.rollUp = function(pts, text) { 39810 var baseRow = this.displayed_[BOTTOM_ROW]; 39811 39812 baseRow += text; 39813 this.displayed_[BOTTOM_ROW] = baseRow; 39814 39815}; 39816 39817Cea608Stream.prototype.shiftRowsUp_ = function() { 39818 var i; 39819 // clear out inactive rows 39820 for (i = 0; i < this.topRow_; i++) { 39821 this.displayed_[i] = ''; 39822 } 39823 // shift displayed rows up 39824 for (i = this.topRow_; i < BOTTOM_ROW; i++) { 39825 this.displayed_[i] = this.displayed_[i + 1]; 39826 } 39827 // clear out the bottom row 39828 this.displayed_[BOTTOM_ROW] = ''; 39829}; 39830 39831// paintOn mode is not implemented 39832Cea608Stream.prototype.paintOn = function() {}; 39833 39834// exports 39835module.exports = { 39836 CaptionStream: CaptionStream, 39837 Cea608Stream: Cea608Stream 39838}; 39839 39840},{"../utils/stream":62}],50:[function(require,module,exports){ 39841/** 39842 * mux.js 39843 * 39844 * Copyright (c) 2015 Brightcove 39845 * All rights reserved. 39846 * 39847 * A stream-based mp2t to mp4 converter. This utility can be used to 39848 * deliver mp4s to a SourceBuffer on platforms that support native 39849 * Media Source Extensions. 39850 */ 39851'use strict'; 39852var Stream = require('../utils/stream.js'), 39853 CaptionStream = require('./caption-stream'), 39854 StreamTypes = require('./stream-types'), 39855 TimestampRolloverStream = require('./timestamp-rollover-stream').TimestampRolloverStream; 39856 39857var m2tsStreamTypes = require('./stream-types.js'); 39858 39859// object types 39860var TransportPacketStream, TransportParseStream, ElementaryStream; 39861 39862// constants 39863var 39864 MP2T_PACKET_LENGTH = 188, // bytes 39865 SYNC_BYTE = 0x47; 39866 39867/** 39868 * Splits an incoming stream of binary data into MPEG-2 Transport 39869 * Stream packets. 39870 */ 39871TransportPacketStream = function() { 39872 var 39873 buffer = new Uint8Array(MP2T_PACKET_LENGTH), 39874 bytesInBuffer = 0; 39875 39876 TransportPacketStream.prototype.init.call(this); 39877 39878 // Deliver new bytes to the stream. 39879 39880 this.push = function(bytes) { 39881 var 39882 startIndex = 0, 39883 endIndex = MP2T_PACKET_LENGTH, 39884 everything; 39885 39886 // If there are bytes remaining from the last segment, prepend them to the 39887 // bytes that were pushed in 39888 if (bytesInBuffer) { 39889 everything = new Uint8Array(bytes.byteLength + bytesInBuffer); 39890 everything.set(buffer.subarray(0, bytesInBuffer)); 39891 everything.set(bytes, bytesInBuffer); 39892 bytesInBuffer = 0; 39893 } else { 39894 everything = bytes; 39895 } 39896 39897 // While we have enough data for a packet 39898 while (endIndex < everything.byteLength) { 39899 // Look for a pair of start and end sync bytes in the data.. 39900 if (everything[startIndex] === SYNC_BYTE && everything[endIndex] === SYNC_BYTE) { 39901 // We found a packet so emit it and jump one whole packet forward in 39902 // the stream 39903 this.trigger('data', everything.subarray(startIndex, endIndex)); 39904 startIndex += MP2T_PACKET_LENGTH; 39905 endIndex += MP2T_PACKET_LENGTH; 39906 continue; 39907 } 39908 // If we get here, we have somehow become de-synchronized and we need to step 39909 // forward one byte at a time until we find a pair of sync bytes that denote 39910 // a packet 39911 startIndex++; 39912 endIndex++; 39913 } 39914 39915 // If there was some data left over at the end of the segment that couldn't 39916 // possibly be a whole packet, keep it because it might be the start of a packet 39917 // that continues in the next segment 39918 if (startIndex < everything.byteLength) { 39919 buffer.set(everything.subarray(startIndex), 0); 39920 bytesInBuffer = everything.byteLength - startIndex; 39921 } 39922 }; 39923 39924 this.flush = function() { 39925 // If the buffer contains a whole packet when we are being flushed, emit it 39926 // and empty the buffer. Otherwise hold onto the data because it may be 39927 // important for decoding the next segment 39928 if (bytesInBuffer === MP2T_PACKET_LENGTH && buffer[0] === SYNC_BYTE) { 39929 this.trigger('data', buffer); 39930 bytesInBuffer = 0; 39931 } 39932 this.trigger('done'); 39933 }; 39934}; 39935TransportPacketStream.prototype = new Stream(); 39936 39937/** 39938 * Accepts an MP2T TransportPacketStream and emits data events with parsed 39939 * forms of the individual transport stream packets. 39940 */ 39941TransportParseStream = function() { 39942 var parsePsi, parsePat, parsePmt, self; 39943 TransportParseStream.prototype.init.call(this); 39944 self = this; 39945 39946 this.packetsWaitingForPmt = []; 39947 this.programMapTable = undefined; 39948 39949 parsePsi = function(payload, psi) { 39950 var offset = 0; 39951 39952 // PSI packets may be split into multiple sections and those 39953 // sections may be split into multiple packets. If a PSI 39954 // section starts in this packet, the payload_unit_start_indicator 39955 // will be true and the first byte of the payload will indicate 39956 // the offset from the current position to the start of the 39957 // section. 39958 if (psi.payloadUnitStartIndicator) { 39959 offset += payload[offset] + 1; 39960 } 39961 39962 if (psi.type === 'pat') { 39963 parsePat(payload.subarray(offset), psi); 39964 } else { 39965 parsePmt(payload.subarray(offset), psi); 39966 } 39967 }; 39968 39969 parsePat = function(payload, pat) { 39970 pat.section_number = payload[7]; // eslint-disable-line camelcase 39971 pat.last_section_number = payload[8]; // eslint-disable-line camelcase 39972 39973 // skip the PSI header and parse the first PMT entry 39974 self.pmtPid = (payload[10] & 0x1F) << 8 | payload[11]; 39975 pat.pmtPid = self.pmtPid; 39976 }; 39977 39978 /** 39979 * Parse out the relevant fields of a Program Map Table (PMT). 39980 * @param payload {Uint8Array} the PMT-specific portion of an MP2T 39981 * packet. The first byte in this array should be the table_id 39982 * field. 39983 * @param pmt {object} the object that should be decorated with 39984 * fields parsed from the PMT. 39985 */ 39986 parsePmt = function(payload, pmt) { 39987 var sectionLength, tableEnd, programInfoLength, offset; 39988 39989 // PMTs can be sent ahead of the time when they should actually 39990 // take effect. We don't believe this should ever be the case 39991 // for HLS but we'll ignore "forward" PMT declarations if we see 39992 // them. Future PMT declarations have the current_next_indicator 39993 // set to zero. 39994 if (!(payload[5] & 0x01)) { 39995 return; 39996 } 39997 39998 // overwrite any existing program map table 39999 self.programMapTable = {}; 40000 40001 // the mapping table ends at the end of the current section 40002 sectionLength = (payload[1] & 0x0f) << 8 | payload[2]; 40003 tableEnd = 3 + sectionLength - 4; 40004 40005 // to determine where the table is, we have to figure out how 40006 // long the program info descriptors are 40007 programInfoLength = (payload[10] & 0x0f) << 8 | payload[11]; 40008 40009 // advance the offset to the first entry in the mapping table 40010 offset = 12 + programInfoLength; 40011 while (offset < tableEnd) { 40012 // add an entry that maps the elementary_pid to the stream_type 40013 self.programMapTable[(payload[offset + 1] & 0x1F) << 8 | payload[offset + 2]] = payload[offset]; 40014 40015 // move to the next table entry 40016 // skip past the elementary stream descriptors, if present 40017 offset += ((payload[offset + 3] & 0x0F) << 8 | payload[offset + 4]) + 5; 40018 } 40019 40020 // record the map on the packet as well 40021 pmt.programMapTable = self.programMapTable; 40022 40023 // if there are any packets waiting for a PMT to be found, process them now 40024 while (self.packetsWaitingForPmt.length) { 40025 self.processPes_.apply(self, self.packetsWaitingForPmt.shift()); 40026 } 40027 }; 40028 40029 /** 40030 * Deliver a new MP2T packet to the stream. 40031 */ 40032 this.push = function(packet) { 40033 var 40034 result = {}, 40035 offset = 4; 40036 40037 result.payloadUnitStartIndicator = !!(packet[1] & 0x40); 40038 40039 // pid is a 13-bit field starting at the last bit of packet[1] 40040 result.pid = packet[1] & 0x1f; 40041 result.pid <<= 8; 40042 result.pid |= packet[2]; 40043 40044 // if an adaption field is present, its length is specified by the 40045 // fifth byte of the TS packet header. The adaptation field is 40046 // used to add stuffing to PES packets that don't fill a complete 40047 // TS packet, and to specify some forms of timing and control data 40048 // that we do not currently use. 40049 if (((packet[3] & 0x30) >>> 4) > 0x01) { 40050 offset += packet[offset] + 1; 40051 } 40052 40053 // parse the rest of the packet based on the type 40054 if (result.pid === 0) { 40055 result.type = 'pat'; 40056 parsePsi(packet.subarray(offset), result); 40057 this.trigger('data', result); 40058 } else if (result.pid === this.pmtPid) { 40059 result.type = 'pmt'; 40060 parsePsi(packet.subarray(offset), result); 40061 this.trigger('data', result); 40062 } else if (this.programMapTable === undefined) { 40063 // When we have not seen a PMT yet, defer further processing of 40064 // PES packets until one has been parsed 40065 this.packetsWaitingForPmt.push([packet, offset, result]); 40066 } else { 40067 this.processPes_(packet, offset, result); 40068 } 40069 }; 40070 40071 this.processPes_ = function(packet, offset, result) { 40072 result.streamType = this.programMapTable[result.pid]; 40073 result.type = 'pes'; 40074 result.data = packet.subarray(offset); 40075 40076 this.trigger('data', result); 40077 }; 40078 40079}; 40080TransportParseStream.prototype = new Stream(); 40081TransportParseStream.STREAM_TYPES = { 40082 h264: 0x1b, 40083 adts: 0x0f 40084}; 40085 40086/** 40087 * Reconsistutes program elementary stream (PES) packets from parsed 40088 * transport stream packets. That is, if you pipe an 40089 * mp2t.TransportParseStream into a mp2t.ElementaryStream, the output 40090 * events will be events which capture the bytes for individual PES 40091 * packets plus relevant metadata that has been extracted from the 40092 * container. 40093 */ 40094ElementaryStream = function() { 40095 var 40096 self = this, 40097 // PES packet fragments 40098 video = { 40099 data: [], 40100 size: 0 40101 }, 40102 audio = { 40103 data: [], 40104 size: 0 40105 }, 40106 timedMetadata = { 40107 data: [], 40108 size: 0 40109 }, 40110 parsePes = function(payload, pes) { 40111 var ptsDtsFlags; 40112 40113 // get the packet length, this will be 0 for video 40114 pes.packetLength = 6 + ((payload[4] << 8) | payload[5]); 40115 40116 // find out if this packets starts a new keyframe 40117 pes.dataAlignmentIndicator = (payload[6] & 0x04) !== 0; 40118 // PES packets may be annotated with a PTS value, or a PTS value 40119 // and a DTS value. Determine what combination of values is 40120 // available to work with. 40121 ptsDtsFlags = payload[7]; 40122 40123 // PTS and DTS are normally stored as a 33-bit number. Javascript 40124 // performs all bitwise operations on 32-bit integers but javascript 40125 // supports a much greater range (52-bits) of integer using standard 40126 // mathematical operations. 40127 // We construct a 31-bit value using bitwise operators over the 31 40128 // most significant bits and then multiply by 4 (equal to a left-shift 40129 // of 2) before we add the final 2 least significant bits of the 40130 // timestamp (equal to an OR.) 40131 if (ptsDtsFlags & 0xC0) { 40132 // the PTS and DTS are not written out directly. For information 40133 // on how they are encoded, see 40134 // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html 40135 pes.pts = (payload[9] & 0x0E) << 27 | 40136 (payload[10] & 0xFF) << 20 | 40137 (payload[11] & 0xFE) << 12 | 40138 (payload[12] & 0xFF) << 5 | 40139 (payload[13] & 0xFE) >>> 3; 40140 pes.pts *= 4; // Left shift by 2 40141 pes.pts += (payload[13] & 0x06) >>> 1; // OR by the two LSBs 40142 pes.dts = pes.pts; 40143 if (ptsDtsFlags & 0x40) { 40144 pes.dts = (payload[14] & 0x0E) << 27 | 40145 (payload[15] & 0xFF) << 20 | 40146 (payload[16] & 0xFE) << 12 | 40147 (payload[17] & 0xFF) << 5 | 40148 (payload[18] & 0xFE) >>> 3; 40149 pes.dts *= 4; // Left shift by 2 40150 pes.dts += (payload[18] & 0x06) >>> 1; // OR by the two LSBs 40151 } 40152 } 40153 // the data section starts immediately after the PES header. 40154 // pes_header_data_length specifies the number of header bytes 40155 // that follow the last byte of the field. 40156 pes.data = payload.subarray(9 + payload[8]); 40157 }, 40158 flushStream = function(stream, type, forceFlush) { 40159 var 40160 packetData = new Uint8Array(stream.size), 40161 event = { 40162 type: type 40163 }, 40164 i = 0, 40165 offset = 0, 40166 packetFlushable = false, 40167 fragment; 40168 40169 // do nothing if there is not enough buffered data for a complete 40170 // PES header 40171 if (!stream.data.length || stream.size < 9) { 40172 return; 40173 } 40174 event.trackId = stream.data[0].pid; 40175 40176 // reassemble the packet 40177 for (i = 0; i < stream.data.length; i++) { 40178 fragment = stream.data[i]; 40179 40180 packetData.set(fragment.data, offset); 40181 offset += fragment.data.byteLength; 40182 } 40183 40184 // parse assembled packet's PES header 40185 parsePes(packetData, event); 40186 40187 // non-video PES packets MUST have a non-zero PES_packet_length 40188 // check that they match before we do a flush 40189 packetFlushable = type === 'video' || event.packetLength === stream.size; 40190 40191 // flush pending packets if the conditions are right 40192 if (forceFlush || packetFlushable) { 40193 stream.size = 0; 40194 stream.data.length = 0; 40195 } 40196 40197 // only emit packets that are complete. this is to avoid assembling 40198 // incomplete PES packets due to poor segmentation 40199 if (packetFlushable) { 40200 self.trigger('data', event); 40201 } 40202 }; 40203 40204 ElementaryStream.prototype.init.call(this); 40205 40206 this.push = function(data) { 40207 ({ 40208 pat: function() { 40209 // we have to wait for the PMT to arrive as well before we 40210 // have any meaningful metadata 40211 }, 40212 pes: function() { 40213 var stream, streamType; 40214 40215 switch (data.streamType) { 40216 case StreamTypes.H264_STREAM_TYPE: 40217 case m2tsStreamTypes.H264_STREAM_TYPE: 40218 stream = video; 40219 streamType = 'video'; 40220 break; 40221 case StreamTypes.ADTS_STREAM_TYPE: 40222 stream = audio; 40223 streamType = 'audio'; 40224 break; 40225 case StreamTypes.METADATA_STREAM_TYPE: 40226 stream = timedMetadata; 40227 streamType = 'timed-metadata'; 40228 break; 40229 default: 40230 // ignore unknown stream types 40231 return; 40232 } 40233 40234 // if a new packet is starting, we can flush the completed 40235 // packet 40236 if (data.payloadUnitStartIndicator) { 40237 flushStream(stream, streamType, true); 40238 } 40239 40240 // buffer this fragment until we are sure we've received the 40241 // complete payload 40242 stream.data.push(data); 40243 stream.size += data.data.byteLength; 40244 }, 40245 pmt: function() { 40246 var 40247 event = { 40248 type: 'metadata', 40249 tracks: [] 40250 }, 40251 programMapTable = data.programMapTable, 40252 k, 40253 track; 40254 40255 // translate streams to tracks 40256 for (k in programMapTable) { 40257 if (programMapTable.hasOwnProperty(k)) { 40258 track = { 40259 timelineStartInfo: { 40260 baseMediaDecodeTime: 0 40261 } 40262 }; 40263 track.id = +k; 40264 if (programMapTable[k] === m2tsStreamTypes.H264_STREAM_TYPE) { 40265 track.codec = 'avc'; 40266 track.type = 'video'; 40267 } else if (programMapTable[k] === m2tsStreamTypes.ADTS_STREAM_TYPE) { 40268 track.codec = 'adts'; 40269 track.type = 'audio'; 40270 } 40271 event.tracks.push(track); 40272 } 40273 }
vendor: 57,163 bytes, lines 40273-42243
40273 40274 self.trigger('data', event); 40275 } 40276 })[data.type](); 40277 }; 40278 40279 /** 40280 * Flush any remaining input. Video PES packets may be of variable 40281 * length. Normally, the start of a new video packet can trigger the 40282 * finalization of the previous packet. That is not possible if no 40283 * more video is forthcoming, however. In that case, some other 40284 * mechanism (like the end of the file) has to be employed. When it is 40285 * clear that no additional data is forthcoming, calling this method 40286 * will flush the buffered packets. 40287 */ 40288 this.flush = function() { 40289 // !!THIS ORDER IS IMPORTANT!! 40290 // video first then audio 40291 flushStream(video, 'video'); 40292 flushStream(audio, 'audio'); 40293 flushStream(timedMetadata, 'timed-metadata'); 40294 this.trigger('done'); 40295 }; 40296}; 40297ElementaryStream.prototype = new Stream(); 40298 40299var m2ts = { 40300 PAT_PID: 0x0000, 40301 MP2T_PACKET_LENGTH: MP2T_PACKET_LENGTH, 40302 TransportPacketStream: TransportPacketStream, 40303 TransportParseStream: TransportParseStream, 40304 ElementaryStream: ElementaryStream, 40305 TimestampRolloverStream: TimestampRolloverStream, 40306 CaptionStream: CaptionStream.CaptionStream, 40307 Cea608Stream: CaptionStream.Cea608Stream, 40308 MetadataStream: require('./metadata-stream') 40309}; 40310 40311for (var type in StreamTypes) { 40312 if (StreamTypes.hasOwnProperty(type)) { 40313 m2ts[type] = StreamTypes[type]; 40314 } 40315} 40316 40317module.exports = m2ts; 40318 40319},{"../utils/stream.js":62,"./caption-stream":49,"./metadata-stream":51,"./stream-types":53,"./stream-types.js":53,"./timestamp-rollover-stream":54}],51:[function(require,module,exports){ 40320/** 40321 * Accepts program elementary stream (PES) data events and parses out 40322 * ID3 metadata from them, if present. 40323 * @see http://id3.org/id3v2.3.0 40324 */ 40325'use strict'; 40326var 40327 Stream = require('../utils/stream'), 40328 StreamTypes = require('./stream-types'), 40329 // return a percent-encoded representation of the specified byte range 40330 // @see http://en.wikipedia.org/wiki/Percent-encoding 40331 percentEncode = function(bytes, start, end) { 40332 var i, result = ''; 40333 for (i = start; i < end; i++) { 40334 result += '%' + ('00' + bytes[i].toString(16)).slice(-2); 40335 } 40336 return result; 40337 }, 40338 // return the string representation of the specified byte range, 40339 // interpreted as UTf-8. 40340 parseUtf8 = function(bytes, start, end) { 40341 return decodeURIComponent(percentEncode(bytes, start, end)); 40342 }, 40343 // return the string representation of the specified byte range, 40344 // interpreted as ISO-8859-1. 40345 parseIso88591 = function(bytes, start, end) { 40346 return unescape(percentEncode(bytes, start, end)); // jshint ignore:line 40347 }, 40348 parseSyncSafeInteger = function(data) { 40349 return (data[0] << 21) | 40350 (data[1] << 14) | 40351 (data[2] << 7) | 40352 (data[3]); 40353 }, 40354 tagParsers = { 40355 TXXX: function(tag) { 40356 var i; 40357 if (tag.data[0] !== 3) { 40358 // ignore frames with unrecognized character encodings 40359 return; 40360 } 40361 40362 for (i = 1; i < tag.data.length; i++) { 40363 if (tag.data[i] === 0) { 40364 // parse the text fields 40365 tag.description = parseUtf8(tag.data, 1, i); 40366 // do not include the null terminator in the tag value 40367 tag.value = parseUtf8(tag.data, i + 1, tag.data.length).replace(/\0*$/, ''); 40368 break; 40369 } 40370 } 40371 tag.data = tag.value; 40372 }, 40373 WXXX: function(tag) { 40374 var i; 40375 if (tag.data[0] !== 3) { 40376 // ignore frames with unrecognized character encodings 40377 return; 40378 } 40379 40380 for (i = 1; i < tag.data.length; i++) { 40381 if (tag.data[i] === 0) { 40382 // parse the description and URL fields 40383 tag.description = parseUtf8(tag.data, 1, i); 40384 tag.url = parseUtf8(tag.data, i + 1, tag.data.length); 40385 break; 40386 } 40387 } 40388 }, 40389 PRIV: function(tag) { 40390 var i; 40391 40392 for (i = 0; i < tag.data.length; i++) { 40393 if (tag.data[i] === 0) { 40394 // parse the description and URL fields 40395 tag.owner = parseIso88591(tag.data, 0, i); 40396 break; 40397 } 40398 } 40399 tag.privateData = tag.data.subarray(i + 1); 40400 tag.data = tag.privateData; 40401 } 40402 }, 40403 MetadataStream; 40404 40405MetadataStream = function(options) { 40406 var 40407 settings = { 40408 debug: !!(options && options.debug), 40409 40410 // the bytes of the program-level descriptor field in MP2T 40411 // see ISO/IEC 13818-1:2013 (E), section 2.6 "Program and 40412 // program element descriptors" 40413 descriptor: options && options.descriptor 40414 }, 40415 // the total size in bytes of the ID3 tag being parsed 40416 tagSize = 0, 40417 // tag data that is not complete enough to be parsed 40418 buffer = [], 40419 // the total number of bytes currently in the buffer 40420 bufferSize = 0, 40421 i; 40422 40423 MetadataStream.prototype.init.call(this); 40424 40425 // calculate the text track in-band metadata track dispatch type 40426 // https://html.spec.whatwg.org/multipage/embedded-content.html#steps-to-expose-a-media-resource-specific-text-track 40427 this.dispatchType = StreamTypes.METADATA_STREAM_TYPE.toString(16); 40428 if (settings.descriptor) { 40429 for (i = 0; i < settings.descriptor.length; i++) { 40430 this.dispatchType += ('00' + settings.descriptor[i].toString(16)).slice(-2); 40431 } 40432 } 40433 40434 this.push = function(chunk) { 40435 var tag, frameStart, frameSize, frame, i, frameHeader; 40436 if (chunk.type !== 'timed-metadata') { 40437 return; 40438 } 40439 40440 // if data_alignment_indicator is set in the PES header, 40441 // we must have the start of a new ID3 tag. Assume anything 40442 // remaining in the buffer was malformed and throw it out 40443 if (chunk.dataAlignmentIndicator) { 40444 bufferSize = 0; 40445 buffer.length = 0; 40446 } 40447 40448 // ignore events that don't look like ID3 data 40449 if (buffer.length === 0 && 40450 (chunk.data.length < 10 || 40451 chunk.data[0] !== 'I'.charCodeAt(0) || 40452 chunk.data[1] !== 'D'.charCodeAt(0) || 40453 chunk.data[2] !== '3'.charCodeAt(0))) { 40454 if (settings.debug) { 40455 // eslint-disable-next-line no-console 40456 console.log('Skipping unrecognized metadata packet'); 40457 } 40458 return; 40459 } 40460 40461 // add this chunk to the data we've collected so far 40462 40463 buffer.push(chunk); 40464 bufferSize += chunk.data.byteLength; 40465 40466 // grab the size of the entire frame from the ID3 header 40467 if (buffer.length === 1) { 40468 // the frame size is transmitted as a 28-bit integer in the 40469 // last four bytes of the ID3 header. 40470 // The most significant bit of each byte is dropped and the 40471 // results concatenated to recover the actual value. 40472 tagSize = parseSyncSafeInteger(chunk.data.subarray(6, 10)); 40473 40474 // ID3 reports the tag size excluding the header but it's more 40475 // convenient for our comparisons to include it 40476 tagSize += 10; 40477 } 40478 40479 // if the entire frame has not arrived, wait for more data 40480 if (bufferSize < tagSize) { 40481 return; 40482 } 40483 40484 // collect the entire frame so it can be parsed 40485 tag = { 40486 data: new Uint8Array(tagSize), 40487 frames: [], 40488 pts: buffer[0].pts, 40489 dts: buffer[0].dts 40490 }; 40491 for (i = 0; i < tagSize;) { 40492 tag.data.set(buffer[0].data.subarray(0, tagSize - i), i); 40493 i += buffer[0].data.byteLength; 40494 bufferSize -= buffer[0].data.byteLength; 40495 buffer.shift(); 40496 } 40497 40498 // find the start of the first frame and the end of the tag 40499 frameStart = 10; 40500 if (tag.data[5] & 0x40) { 40501 // advance the frame start past the extended header 40502 frameStart += 4; // header size field 40503 frameStart += parseSyncSafeInteger(tag.data.subarray(10, 14)); 40504 40505 // clip any padding off the end 40506 tagSize -= parseSyncSafeInteger(tag.data.subarray(16, 20)); 40507 } 40508 40509 // parse one or more ID3 frames 40510 // http://id3.org/id3v2.3.0#ID3v2_frame_overview 40511 do { 40512 // determine the number of bytes in this frame 40513 frameSize = parseSyncSafeInteger(tag.data.subarray(frameStart + 4, frameStart + 8)); 40514 if (frameSize < 1) { 40515 // eslint-disable-next-line no-console 40516 return console.log('Malformed ID3 frame encountered. Skipping metadata parsing.'); 40517 } 40518 frameHeader = String.fromCharCode(tag.data[frameStart], 40519 tag.data[frameStart + 1], 40520 tag.data[frameStart + 2], 40521 tag.data[frameStart + 3]); 40522 40523 40524 frame = { 40525 id: frameHeader, 40526 data: tag.data.subarray(frameStart + 10, frameStart + frameSize + 10) 40527 }; 40528 frame.key = frame.id; 40529 if (tagParsers[frame.id]) { 40530 tagParsers[frame.id](frame); 40531 40532 // handle the special PRIV frame used to indicate the start 40533 // time for raw AAC data 40534 if (frame.owner === 'com.apple.streaming.transportStreamTimestamp') { 40535 var 40536 d = frame.data, 40537 size = ((d[3] & 0x01) << 30) | 40538 (d[4] << 22) | 40539 (d[5] << 14) | 40540 (d[6] << 6) | 40541 (d[7] >>> 2); 40542 40543 size *= 4; 40544 size += d[7] & 0x03; 40545 frame.timeStamp = size; 40546 // in raw AAC, all subsequent data will be timestamped based 40547 // on the value of this frame 40548 // we couldn't have known the appropriate pts and dts before 40549 // parsing this ID3 tag so set those values now 40550 if (tag.pts === undefined && tag.dts === undefined) { 40551 tag.pts = frame.timeStamp; 40552 tag.dts = frame.timeStamp; 40553 } 40554 this.trigger('timestamp', frame); 40555 } 40556 } 40557 tag.frames.push(frame); 40558 40559 frameStart += 10; // advance past the frame header 40560 frameStart += frameSize; // advance past the frame body 40561 } while (frameStart < tagSize); 40562 this.trigger('data', tag); 40563 }; 40564}; 40565MetadataStream.prototype = new Stream(); 40566 40567module.exports = MetadataStream; 40568 40569},{"../utils/stream":62,"./stream-types":53}],52:[function(require,module,exports){ 40570/** 40571 * mux.js 40572 * 40573 * Copyright (c) 2016 Brightcove 40574 * All rights reserved. 40575 * 40576 * Utilities to detect basic properties and metadata about TS Segments. 40577 */ 40578'use strict'; 40579 40580var StreamTypes = require('./stream-types.js'); 40581 40582var parsePid = function(packet) { 40583 var pid = packet[1] & 0x1f; 40584 pid <<= 8; 40585 pid |= packet[2]; 40586 return pid; 40587}; 40588 40589var parsePayloadUnitStartIndicator = function(packet) { 40590 return !!(packet[1] & 0x40); 40591}; 40592 40593var parseAdaptionField = function(packet) { 40594 var offset = 0; 40595 // if an adaption field is present, its length is specified by the 40596 // fifth byte of the TS packet header. The adaptation field is 40597 // used to add stuffing to PES packets that don't fill a complete 40598 // TS packet, and to specify some forms of timing and control data 40599 // that we do not currently use. 40600 if (((packet[3] & 0x30) >>> 4) > 0x01) { 40601 offset += packet[4] + 1; 40602 } 40603 return offset; 40604}; 40605 40606var parseType = function(packet, pmtPid) { 40607 var pid = parsePid(packet); 40608 if (pid === 0) { 40609 return 'pat'; 40610 } else if (pid === pmtPid) { 40611 return 'pmt'; 40612 } else if (pmtPid) { 40613 return 'pes'; 40614 } 40615 return null; 40616}; 40617 40618var parsePat = function(packet) { 40619 var pusi = parsePayloadUnitStartIndicator(packet); 40620 var offset = 4 + parseAdaptionField(packet); 40621 40622 if (pusi) { 40623 offset += packet[offset] + 1; 40624 } 40625 40626 return (packet[offset + 10] & 0x1f) << 8 | packet[offset + 11]; 40627}; 40628 40629var parsePmt = function(packet) { 40630 var programMapTable = {}; 40631 var pusi = parsePayloadUnitStartIndicator(packet); 40632 var payloadOffset = 4 + parseAdaptionField(packet); 40633 40634 if (pusi) { 40635 payloadOffset += packet[payloadOffset] + 1; 40636 } 40637 40638 // PMTs can be sent ahead of the time when they should actually 40639 // take effect. We don't believe this should ever be the case 40640 // for HLS but we'll ignore "forward" PMT declarations if we see 40641 // them. Future PMT declarations have the current_next_indicator 40642 // set to zero. 40643 if (!(packet[payloadOffset + 5] & 0x01)) { 40644 return; 40645 } 40646 40647 var sectionLength, tableEnd, programInfoLength; 40648 // the mapping table ends at the end of the current section 40649 sectionLength = (packet[payloadOffset + 1] & 0x0f) << 8 | packet[payloadOffset + 2]; 40650 tableEnd = 3 + sectionLength - 4; 40651 40652 // to determine where the table is, we have to figure out how 40653 // long the program info descriptors are 40654 programInfoLength = (packet[payloadOffset + 10] & 0x0f) << 8 | packet[payloadOffset + 11]; 40655 40656 // advance the offset to the first entry in the mapping table 40657 var offset = 12 + programInfoLength; 40658 while (offset < tableEnd) { 40659 var i = payloadOffset + offset; 40660 // add an entry that maps the elementary_pid to the stream_type 40661 programMapTable[(packet[i + 1] & 0x1F) << 8 | packet[i + 2]] = packet[i]; 40662 40663 // move to the next table entry 40664 // skip past the elementary stream descriptors, if present 40665 offset += ((packet[i + 3] & 0x0F) << 8 | packet[i + 4]) + 5; 40666 } 40667 return programMapTable; 40668}; 40669 40670var parsePesType = function(packet, programMapTable) { 40671 var pid = parsePid(packet); 40672 var type = programMapTable[pid]; 40673 switch (type) { 40674 case StreamTypes.H264_STREAM_TYPE: 40675 return 'video'; 40676 case StreamTypes.ADTS_STREAM_TYPE: 40677 return 'audio'; 40678 case StreamTypes.METADATA_STREAM_TYPE: 40679 return 'timed-metadata'; 40680 default: 40681 return null; 40682 } 40683}; 40684 40685var parsePesTime = function(packet) { 40686 var pusi = parsePayloadUnitStartIndicator(packet); 40687 if (!pusi) { 40688 return null; 40689 } 40690 40691 var offset = 4 + parseAdaptionField(packet); 40692 40693 if (offset >= packet.byteLength) { 40694 // From the H 222.0 MPEG-TS spec 40695 // "For transport stream packets carrying PES packets, stuffing is needed when there 40696 // is insufficient PES packet data to completely fill the transport stream packet 40697 // payload bytes. Stuffing is accomplished by defining an adaptation field longer than 40698 // the sum of the lengths of the data elements in it, so that the payload bytes 40699 // remaining after the adaptation field exactly accommodates the available PES packet 40700 // data." 40701 // 40702 // If the offset is >= the length of the packet, then the packet contains no data 40703 // and instead is just adaption field stuffing bytes 40704 return null; 40705 } 40706 40707 var pes = null; 40708 var ptsDtsFlags; 40709 40710 // PES packets may be annotated with a PTS value, or a PTS value 40711 // and a DTS value. Determine what combination of values is 40712 // available to work with. 40713 ptsDtsFlags = packet[offset + 7]; 40714 40715 // PTS and DTS are normally stored as a 33-bit number. Javascript 40716 // performs all bitwise operations on 32-bit integers but javascript 40717 // supports a much greater range (52-bits) of integer using standard 40718 // mathematical operations. 40719 // We construct a 31-bit value using bitwise operators over the 31 40720 // most significant bits and then multiply by 4 (equal to a left-shift 40721 // of 2) before we add the final 2 least significant bits of the 40722 // timestamp (equal to an OR.) 40723 if (ptsDtsFlags & 0xC0) { 40724 pes = {}; 40725 // the PTS and DTS are not written out directly. For information 40726 // on how they are encoded, see 40727 // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html 40728 pes.pts = (packet[offset + 9] & 0x0E) << 27 | 40729 (packet[offset + 10] & 0xFF) << 20 | 40730 (packet[offset + 11] & 0xFE) << 12 | 40731 (packet[offset + 12] & 0xFF) << 5 | 40732 (packet[offset + 13] & 0xFE) >>> 3; 40733 pes.pts *= 4; // Left shift by 2 40734 pes.pts += (packet[offset + 13] & 0x06) >>> 1; // OR by the two LSBs 40735 pes.dts = pes.pts; 40736 if (ptsDtsFlags & 0x40) { 40737 pes.dts = (packet[offset + 14] & 0x0E) << 27 | 40738 (packet[offset + 15] & 0xFF) << 20 | 40739 (packet[offset + 16] & 0xFE) << 12 | 40740 (packet[offset + 17] & 0xFF) << 5 | 40741 (packet[offset + 18] & 0xFE) >>> 3; 40742 pes.dts *= 4; // Left shift by 2 40743 pes.dts += (packet[offset + 18] & 0x06) >>> 1; // OR by the two LSBs 40744 } 40745 } 40746 return pes; 40747}; 40748 40749var parseNalUnitType = function(type) { 40750 switch (type) { 40751 case 0x05: 40752 return 'slice_layer_without_partitioning_rbsp_idr'; 40753 case 0x06: 40754 return 'sei_rbsp'; 40755 case 0x07: 40756 return 'seq_parameter_set_rbsp'; 40757 case 0x08: 40758 return 'pic_parameter_set_rbsp'; 40759 case 0x09: 40760 return 'access_unit_delimiter_rbsp'; 40761 default: 40762 return null; 40763 } 40764}; 40765 40766var videoPacketContainsKeyFrame = function(packet) { 40767 var offset = 4 + parseAdaptionField(packet); 40768 var frameBuffer = packet.subarray(offset); 40769 var frameI = 0; 40770 var frameSyncPoint = 0; 40771 var foundKeyFrame = false; 40772 var nalType; 40773 40774 // advance the sync point to a NAL start, if necessary 40775 for (; frameSyncPoint < frameBuffer.byteLength - 3; frameSyncPoint++) { 40776 if (frameBuffer[frameSyncPoint + 2] === 1) { 40777 // the sync point is properly aligned 40778 frameI = frameSyncPoint + 5; 40779 break; 40780 } 40781 } 40782 40783 while (frameI < frameBuffer.byteLength) { 40784 // look at the current byte to determine if we've hit the end of 40785 // a NAL unit boundary 40786 switch (frameBuffer[frameI]) { 40787 case 0: 40788 // skip past non-sync sequences 40789 if (frameBuffer[frameI - 1] !== 0) { 40790 frameI += 2; 40791 break; 40792 } else if (frameBuffer[frameI - 2] !== 0) { 40793 frameI++; 40794 break; 40795 } 40796 40797 if (frameSyncPoint + 3 !== frameI - 2) { 40798 nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f); 40799 if (nalType === 'slice_layer_without_partitioning_rbsp_idr') { 40800 foundKeyFrame = true; 40801 } 40802 } 40803 40804 // drop trailing zeroes 40805 do { 40806 frameI++; 40807 } while (frameBuffer[frameI] !== 1 && frameI < frameBuffer.length); 40808 frameSyncPoint = frameI - 2; 40809 frameI += 3; 40810 break; 40811 case 1: 40812 // skip past non-sync sequences 40813 if (frameBuffer[frameI - 1] !== 0 || 40814 frameBuffer[frameI - 2] !== 0) { 40815 frameI += 3; 40816 break; 40817 } 40818 40819 nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f); 40820 if (nalType === 'slice_layer_without_partitioning_rbsp_idr') { 40821 foundKeyFrame = true; 40822 } 40823 frameSyncPoint = frameI - 2; 40824 frameI += 3; 40825 break; 40826 default: 40827 // the current byte isn't a one or zero, so it cannot be part 40828 // of a sync sequence 40829 frameI += 3; 40830 break; 40831 } 40832 } 40833 frameBuffer = frameBuffer.subarray(frameSyncPoint); 40834 frameI -= frameSyncPoint; 40835 frameSyncPoint = 0; 40836 // parse the final nal 40837 if (frameBuffer && frameBuffer.byteLength > 3) { 40838 nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f); 40839 if (nalType === 'slice_layer_without_partitioning_rbsp_idr') { 40840 foundKeyFrame = true; 40841 } 40842 } 40843 40844 return foundKeyFrame; 40845}; 40846 40847 40848module.exports = { 40849 parseType: parseType, 40850 parsePat: parsePat, 40851 parsePmt: parsePmt, 40852 parsePayloadUnitStartIndicator: parsePayloadUnitStartIndicator, 40853 parsePesType: parsePesType, 40854 parsePesTime: parsePesTime, 40855 videoPacketContainsKeyFrame: videoPacketContainsKeyFrame 40856}; 40857 40858},{"./stream-types.js":53}],53:[function(require,module,exports){ 40859'use strict'; 40860 40861module.exports = { 40862 H264_STREAM_TYPE: 0x1B, 40863 ADTS_STREAM_TYPE: 0x0F, 40864 METADATA_STREAM_TYPE: 0x15 40865}; 40866 40867},{}],54:[function(require,module,exports){ 40868/** 40869 * mux.js 40870 * 40871 * Copyright (c) 2016 Brightcove 40872 * All rights reserved. 40873 * 40874 * Accepts program elementary stream (PES) data events and corrects 40875 * decode and presentation time stamps to account for a rollover 40876 * of the 33 bit value. 40877 */ 40878 40879'use strict'; 40880 40881var Stream = require('../utils/stream'); 40882 40883var MAX_TS = 8589934592; 40884 40885var RO_THRESH = 4294967296; 40886 40887var handleRollover = function(value, reference) { 40888 var direction = 1; 40889 40890 if (value > reference) { 40891 // If the current timestamp value is greater than our reference timestamp and we detect a 40892 // timestamp rollover, this means the roll over is happening in the opposite direction. 40893 // Example scenario: Enter a long stream/video just after a rollover occurred. The reference 40894 // point will be set to a small number, e.g. 1. The user then seeks backwards over the 40895 // rollover point. In loading this segment, the timestamp values will be very large, 40896 // e.g. 2^33 - 1. Since this comes before the data we loaded previously, we want to adjust 40897 // the time stamp to be `value - 2^33`. 40898 direction = -1; 40899 } 40900 40901 // Note: A seek forwards or back that is greater than the RO_THRESH (2^32, ~13 hours) will 40902 // cause an incorrect adjustment. 40903 while (Math.abs(reference - value) > RO_THRESH) { 40904 value += (direction * MAX_TS); 40905 } 40906 40907 return value; 40908}; 40909 40910var TimestampRolloverStream = function(type) { 40911 var lastDTS, referenceDTS; 40912 40913 TimestampRolloverStream.prototype.init.call(this); 40914 40915 this.type_ = type; 40916 40917 this.push = function(data) { 40918 if (data.type !== this.type_) { 40919 return; 40920 } 40921 40922 if (referenceDTS === undefined) { 40923 referenceDTS = data.dts; 40924 } 40925 40926 data.dts = handleRollover(data.dts, referenceDTS); 40927 data.pts = handleRollover(data.pts, referenceDTS); 40928 40929 lastDTS = data.dts; 40930 40931 this.trigger('data', data); 40932 }; 40933 40934 this.flush = function() { 40935 referenceDTS = lastDTS; 40936 this.trigger('done'); 40937 }; 40938 40939 this.discontinuity = function() { 40940 referenceDTS = void 0; 40941 lastDTS = void 0; 40942 }; 40943 40944}; 40945 40946TimestampRolloverStream.prototype = new Stream(); 40947 40948module.exports = { 40949 TimestampRolloverStream: TimestampRolloverStream, 40950 handleRollover: handleRollover 40951}; 40952 40953},{"../utils/stream":62}],55:[function(require,module,exports){ 40954module.exports = { 40955 generator: require('./mp4-generator'), 40956 Transmuxer: require('./transmuxer').Transmuxer, 40957 AudioSegmentStream: require('./transmuxer').AudioSegmentStream, 40958 VideoSegmentStream: require('./transmuxer').VideoSegmentStream 40959}; 40960 40961},{"./mp4-generator":56,"./transmuxer":58}],56:[function(require,module,exports){ 40962/** 40963 * mux.js 40964 * 40965 * Copyright (c) 2015 Brightcove 40966 * All rights reserved. 40967 * 40968 * Functions that generate fragmented MP4s suitable for use with Media 40969 * Source Extensions. 40970 */ 40971'use strict'; 40972 40973var UINT32_MAX = Math.pow(2, 32) - 1; 40974 40975var box, dinf, esds, ftyp, mdat, mfhd, minf, moof, moov, mvex, mvhd, 40976 trak, tkhd, mdia, mdhd, hdlr, sdtp, stbl, stsd, traf, trex, 40977 trun, types, MAJOR_BRAND, MINOR_VERSION, AVC1_BRAND, VIDEO_HDLR, 40978 AUDIO_HDLR, HDLR_TYPES, VMHD, SMHD, DREF, STCO, STSC, STSZ, STTS; 40979 40980// pre-calculate constants 40981(function() { 40982 var i; 40983 types = { 40984 avc1: [], // codingname 40985 avcC: [], 40986 btrt: [], 40987 dinf: [], 40988 dref: [], 40989 esds: [], 40990 ftyp: [], 40991 hdlr: [], 40992 mdat: [], 40993 mdhd: [], 40994 mdia: [], 40995 mfhd: [], 40996 minf: [], 40997 moof: [], 40998 moov: [], 40999 mp4a: [], // codingname 41000 mvex: [], 41001 mvhd: [], 41002 sdtp: [], 41003 smhd: [], 41004 stbl: [], 41005 stco: [], 41006 stsc: [], 41007 stsd: [], 41008 stsz: [], 41009 stts: [], 41010 styp: [], 41011 tfdt: [], 41012 tfhd: [], 41013 traf: [], 41014 trak: [], 41015 trun: [], 41016 trex: [], 41017 tkhd: [], 41018 vmhd: [] 41019 }; 41020 41021 // In environments where Uint8Array is undefined (e.g., IE8), skip set up so that we 41022 // don't throw an error 41023 if (typeof Uint8Array === 'undefined') { 41024 return; 41025 } 41026 41027 for (i in types) { 41028 if (types.hasOwnProperty(i)) { 41029 types[i] = [ 41030 i.charCodeAt(0), 41031 i.charCodeAt(1), 41032 i.charCodeAt(2), 41033 i.charCodeAt(3) 41034 ]; 41035 } 41036 } 41037 41038 MAJOR_BRAND = new Uint8Array([ 41039 'i'.charCodeAt(0), 41040 's'.charCodeAt(0), 41041 'o'.charCodeAt(0), 41042 'm'.charCodeAt(0) 41043 ]); 41044 AVC1_BRAND = new Uint8Array([ 41045 'a'.charCodeAt(0), 41046 'v'.charCodeAt(0), 41047 'c'.charCodeAt(0), 41048 '1'.charCodeAt(0) 41049 ]); 41050 MINOR_VERSION = new Uint8Array([0, 0, 0, 1]); 41051 VIDEO_HDLR = new Uint8Array([ 41052 0x00, // version 0 41053 0x00, 0x00, 0x00, // flags 41054 0x00, 0x00, 0x00, 0x00, // pre_defined 41055 0x76, 0x69, 0x64, 0x65, // handler_type: 'vide' 41056 0x00, 0x00, 0x00, 0x00, // reserved 41057 0x00, 0x00, 0x00, 0x00, // reserved 41058 0x00, 0x00, 0x00, 0x00, // reserved 41059 0x56, 0x69, 0x64, 0x65, 41060 0x6f, 0x48, 0x61, 0x6e, 41061 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'VideoHandler' 41062 ]); 41063 AUDIO_HDLR = new Uint8Array([ 41064 0x00, // version 0 41065 0x00, 0x00, 0x00, // flags 41066 0x00, 0x00, 0x00, 0x00, // pre_defined 41067 0x73, 0x6f, 0x75, 0x6e, // handler_type: 'soun' 41068 0x00, 0x00, 0x00, 0x00, // reserved 41069 0x00, 0x00, 0x00, 0x00, // reserved 41070 0x00, 0x00, 0x00, 0x00, // reserved 41071 0x53, 0x6f, 0x75, 0x6e, 41072 0x64, 0x48, 0x61, 0x6e, 41073 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'SoundHandler' 41074 ]); 41075 HDLR_TYPES = { 41076 video: VIDEO_HDLR, 41077 audio: AUDIO_HDLR 41078 }; 41079 DREF = new Uint8Array([ 41080 0x00, // version 0 41081 0x00, 0x00, 0x00, // flags 41082 0x00, 0x00, 0x00, 0x01, // entry_count 41083 0x00, 0x00, 0x00, 0x0c, // entry_size 41084 0x75, 0x72, 0x6c, 0x20, // 'url' type 41085 0x00, // version 0 41086 0x00, 0x00, 0x01 // entry_flags 41087 ]); 41088 SMHD = new Uint8Array([ 41089 0x00, // version 41090 0x00, 0x00, 0x00, // flags 41091 0x00, 0x00, // balance, 0 means centered 41092 0x00, 0x00 // reserved 41093 ]); 41094 STCO = new Uint8Array([ 41095 0x00, // version 41096 0x00, 0x00, 0x00, // flags 41097 0x00, 0x00, 0x00, 0x00 // entry_count 41098 ]); 41099 STSC = STCO; 41100 STSZ = new Uint8Array([ 41101 0x00, // version 41102 0x00, 0x00, 0x00, // flags 41103 0x00, 0x00, 0x00, 0x00, // sample_size 41104 0x00, 0x00, 0x00, 0x00 // sample_count 41105 ]); 41106 STTS = STCO; 41107 VMHD = new Uint8Array([ 41108 0x00, // version 41109 0x00, 0x00, 0x01, // flags 41110 0x00, 0x00, // graphicsmode 41111 0x00, 0x00, 41112 0x00, 0x00, 41113 0x00, 0x00 // opcolor 41114 ]); 41115}()); 41116 41117box = function(type) { 41118 var 41119 payload = [], 41120 size = 0, 41121 i, 41122 result, 41123 view; 41124 41125 for (i = 1; i < arguments.length; i++) { 41126 payload.push(arguments[i]); 41127 } 41128 41129 i = payload.length; 41130 41131 // calculate the total size we need to allocate 41132 while (i--) { 41133 size += payload[i].byteLength; 41134 } 41135 result = new Uint8Array(size + 8); 41136 view = new DataView(result.buffer, result.byteOffset, result.byteLength); 41137 view.setUint32(0, result.byteLength); 41138 result.set(type, 4); 41139 41140 // copy the payload into the result 41141 for (i = 0, size = 8; i < payload.length; i++) { 41142 result.set(payload[i], size); 41143 size += payload[i].byteLength; 41144 } 41145 return result; 41146}; 41147 41148dinf = function() { 41149 return box(types.dinf, box(types.dref, DREF)); 41150}; 41151 41152esds = function(track) { 41153 return box(types.esds, new Uint8Array([ 41154 0x00, // version 41155 0x00, 0x00, 0x00, // flags 41156 41157 // ES_Descriptor 41158 0x03, // tag, ES_DescrTag 41159 0x19, // length 41160 0x00, 0x00, // ES_ID 41161 0x00, // streamDependenceFlag, URL_flag, reserved, streamPriority 41162 41163 // DecoderConfigDescriptor 41164 0x04, // tag, DecoderConfigDescrTag 41165 0x11, // length 41166 0x40, // object type 41167 0x15, // streamType 41168 0x00, 0x06, 0x00, // bufferSizeDB 41169 0x00, 0x00, 0xda, 0xc0, // maxBitrate 41170 0x00, 0x00, 0xda, 0xc0, // avgBitrate 41171 41172 // DecoderSpecificInfo 41173 0x05, // tag, DecoderSpecificInfoTag 41174 0x02, // length 41175 // ISO/IEC 14496-3, AudioSpecificConfig 41176 // for samplingFrequencyIndex see ISO/IEC 13818-7:2006, 8.1.3.2.2, Table 35 41177 (track.audioobjecttype << 3) | (track.samplingfrequencyindex >>> 1), 41178 (track.samplingfrequencyindex << 7) | (track.channelcount << 3), 41179 0x06, 0x01, 0x02 // GASpecificConfig 41180 ])); 41181}; 41182 41183ftyp = function() { 41184 return box(types.ftyp, MAJOR_BRAND, MINOR_VERSION, MAJOR_BRAND, AVC1_BRAND); 41185}; 41186 41187hdlr = function(type) { 41188 return box(types.hdlr, HDLR_TYPES[type]); 41189}; 41190mdat = function(data) { 41191 return box(types.mdat, data); 41192}; 41193mdhd = function(track) { 41194 var result = new Uint8Array([ 41195 0x00, // version 0 41196 0x00, 0x00, 0x00, // flags 41197 0x00, 0x00, 0x00, 0x02, // creation_time 41198 0x00, 0x00, 0x00, 0x03, // modification_time 41199 0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second 41200 41201 (track.duration >>> 24) & 0xFF, 41202 (track.duration >>> 16) & 0xFF, 41203 (track.duration >>> 8) & 0xFF, 41204 track.duration & 0xFF, // duration 41205 0x55, 0xc4, // 'und' language (undetermined) 41206 0x00, 0x00 41207 ]); 41208 41209 // Use the sample rate from the track metadata, when it is 41210 // defined. The sample rate can be parsed out of an ADTS header, for 41211 // instance. 41212 if (track.samplerate) { 41213 result[12] = (track.samplerate >>> 24) & 0xFF; 41214 result[13] = (track.samplerate >>> 16) & 0xFF; 41215 result[14] = (track.samplerate >>> 8) & 0xFF; 41216 result[15] = (track.samplerate) & 0xFF; 41217 } 41218 41219 return box(types.mdhd, result); 41220}; 41221mdia = function(track) { 41222 return box(types.mdia, mdhd(track), hdlr(track.type), minf(track)); 41223}; 41224mfhd = function(sequenceNumber) { 41225 return box(types.mfhd, new Uint8Array([ 41226 0x00, 41227 0x00, 0x00, 0x00, // flags 41228 (sequenceNumber & 0xFF000000) >> 24, 41229 (sequenceNumber & 0xFF0000) >> 16, 41230 (sequenceNumber & 0xFF00) >> 8, 41231 sequenceNumber & 0xFF // sequence_number 41232 ])); 41233}; 41234minf = function(track) { 41235 return box(types.minf, 41236 track.type === 'video' ? box(types.vmhd, VMHD) : box(types.smhd, SMHD), 41237 dinf(), 41238 stbl(track)); 41239}; 41240moof = function(sequenceNumber, tracks) { 41241 var 41242 trackFragments = [], 41243 i = tracks.length; 41244 // build traf boxes for each track fragment 41245 while (i--) { 41246 trackFragments[i] = traf(tracks[i]); 41247 } 41248 return box.apply(null, [ 41249 types.moof, 41250 mfhd(sequenceNumber) 41251 ].concat(trackFragments)); 41252}; 41253/** 41254 * Returns a movie box. 41255 * @param tracks {array} the tracks associated with this movie 41256 * @see ISO/IEC 14496-12:2012(E), section 8.2.1 41257 */ 41258moov = function(tracks) { 41259 var 41260 i = tracks.length, 41261 boxes = []; 41262 41263 while (i--) { 41264 boxes[i] = trak(tracks[i]); 41265 } 41266 41267 return box.apply(null, [types.moov, mvhd(0xffffffff)].concat(boxes).concat(mvex(tracks))); 41268}; 41269mvex = function(tracks) { 41270 var 41271 i = tracks.length, 41272 boxes = []; 41273 41274 while (i--) { 41275 boxes[i] = trex(tracks[i]); 41276 } 41277 return box.apply(null, [types.mvex].concat(boxes)); 41278}; 41279mvhd = function(duration) { 41280 var 41281 bytes = new Uint8Array([ 41282 0x00, // version 0 41283 0x00, 0x00, 0x00, // flags 41284 0x00, 0x00, 0x00, 0x01, // creation_time 41285 0x00, 0x00, 0x00, 0x02, // modification_time 41286 0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second 41287 (duration & 0xFF000000) >> 24, 41288 (duration & 0xFF0000) >> 16, 41289 (duration & 0xFF00) >> 8, 41290 duration & 0xFF, // duration 41291 0x00, 0x01, 0x00, 0x00, // 1.0 rate 41292 0x01, 0x00, // 1.0 volume 41293 0x00, 0x00, // reserved 41294 0x00, 0x00, 0x00, 0x00, // reserved 41295 0x00, 0x00, 0x00, 0x00, // reserved 41296 0x00, 0x01, 0x00, 0x00, 41297 0x00, 0x00, 0x00, 0x00, 41298 0x00, 0x00, 0x00, 0x00, 41299 0x00, 0x00, 0x00, 0x00, 41300 0x00, 0x01, 0x00, 0x00, 41301 0x00, 0x00, 0x00, 0x00, 41302 0x00, 0x00, 0x00, 0x00, 41303 0x00, 0x00, 0x00, 0x00, 41304 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix 41305 0x00, 0x00, 0x00, 0x00, 41306 0x00, 0x00, 0x00, 0x00, 41307 0x00, 0x00, 0x00, 0x00, 41308 0x00, 0x00, 0x00, 0x00, 41309 0x00, 0x00, 0x00, 0x00, 41310 0x00, 0x00, 0x00, 0x00, // pre_defined 41311 0xff, 0xff, 0xff, 0xff // next_track_ID 41312 ]); 41313 return box(types.mvhd, bytes); 41314}; 41315 41316sdtp = function(track) { 41317 var 41318 samples = track.samples || [], 41319 bytes = new Uint8Array(4 + samples.length), 41320 flags, 41321 i; 41322 41323 // leave the full box header (4 bytes) all zero 41324 41325 // write the sample table 41326 for (i = 0; i < samples.length; i++) { 41327 flags = samples[i].flags; 41328 41329 bytes[i + 4] = (flags.dependsOn << 4) | 41330 (flags.isDependedOn << 2) | 41331 (flags.hasRedundancy); 41332 } 41333 41334 return box(types.sdtp, 41335 bytes); 41336}; 41337 41338stbl = function(track) { 41339 return box(types.stbl, 41340 stsd(track), 41341 box(types.stts, STTS), 41342 box(types.stsc, STSC), 41343 box(types.stsz, STSZ), 41344 box(types.stco, STCO)); 41345}; 41346 41347(function() { 41348 var videoSample, audioSample; 41349 41350 stsd = function(track) { 41351 41352 return box(types.stsd, new Uint8Array([ 41353 0x00, // version 0 41354 0x00, 0x00, 0x00, // flags 41355 0x00, 0x00, 0x00, 0x01 41356 ]), track.type === 'video' ? videoSample(track) : audioSample(track)); 41357 }; 41358 41359 videoSample = function(track) { 41360 var 41361 sps = track.sps || [], 41362 pps = track.pps || [], 41363 sequenceParameterSets = [], 41364 pictureParameterSets = [], 41365 i; 41366 41367 // assemble the SPSs 41368 for (i = 0; i < sps.length; i++) { 41369 sequenceParameterSets.push((sps[i].byteLength & 0xFF00) >>> 8); 41370 sequenceParameterSets.push((sps[i].byteLength & 0xFF)); // sequenceParameterSetLength 41371 sequenceParameterSets = sequenceParameterSets.concat(Array.prototype.slice.call(sps[i])); // SPS 41372 } 41373 41374 // assemble the PPSs 41375 for (i = 0; i < pps.length; i++) { 41376 pictureParameterSets.push((pps[i].byteLength & 0xFF00) >>> 8); 41377 pictureParameterSets.push((pps[i].byteLength & 0xFF)); 41378 pictureParameterSets = pictureParameterSets.concat(Array.prototype.slice.call(pps[i])); 41379 } 41380 41381 return box(types.avc1, new Uint8Array([ 41382 0x00, 0x00, 0x00, 41383 0x00, 0x00, 0x00, // reserved 41384 0x00, 0x01, // data_reference_index 41385 0x00, 0x00, // pre_defined 41386 0x00, 0x00, // reserved 41387 0x00, 0x00, 0x00, 0x00, 41388 0x00, 0x00, 0x00, 0x00, 41389 0x00, 0x00, 0x00, 0x00, // pre_defined 41390 (track.width & 0xff00) >> 8, 41391 track.width & 0xff, // width 41392 (track.height & 0xff00) >> 8, 41393 track.height & 0xff, // height 41394 0x00, 0x48, 0x00, 0x00, // horizresolution 41395 0x00, 0x48, 0x00, 0x00, // vertresolution 41396 0x00, 0x00, 0x00, 0x00, // reserved 41397 0x00, 0x01, // frame_count 41398 0x13, 41399 0x76, 0x69, 0x64, 0x65, 41400 0x6f, 0x6a, 0x73, 0x2d, 41401 0x63, 0x6f, 0x6e, 0x74, 41402 0x72, 0x69, 0x62, 0x2d, 41403 0x68, 0x6c, 0x73, 0x00, 41404 0x00, 0x00, 0x00, 0x00, 41405 0x00, 0x00, 0x00, 0x00, 41406 0x00, 0x00, 0x00, // compressorname 41407 0x00, 0x18, // depth = 24 41408 0x11, 0x11 // pre_defined = -1 41409 ]), box(types.avcC, new Uint8Array([ 41410 0x01, // configurationVersion 41411 track.profileIdc, // AVCProfileIndication 41412 track.profileCompatibility, // profile_compatibility 41413 track.levelIdc, // AVCLevelIndication 41414 0xff // lengthSizeMinusOne, hard-coded to 4 bytes 41415 ].concat([ 41416 sps.length // numOfSequenceParameterSets 41417 ]).concat(sequenceParameterSets).concat([ 41418 pps.length // numOfPictureParameterSets 41419 ]).concat(pictureParameterSets))), // "PPS" 41420 box(types.btrt, new Uint8Array([ 41421 0x00, 0x1c, 0x9c, 0x80, // bufferSizeDB 41422 0x00, 0x2d, 0xc6, 0xc0, // maxBitrate 41423 0x00, 0x2d, 0xc6, 0xc0 41424 ])) // avgBitrate 41425 ); 41426 }; 41427 41428 audioSample = function(track) { 41429 return box(types.mp4a, new Uint8Array([ 41430 41431 // SampleEntry, ISO/IEC 14496-12 41432 0x00, 0x00, 0x00, 41433 0x00, 0x00, 0x00, // reserved 41434 0x00, 0x01, // data_reference_index 41435 41436 // AudioSampleEntry, ISO/IEC 14496-12 41437 0x00, 0x00, 0x00, 0x00, // reserved 41438 0x00, 0x00, 0x00, 0x00, // reserved 41439 (track.channelcount & 0xff00) >> 8, 41440 (track.channelcount & 0xff), // channelcount 41441 41442 (track.samplesize & 0xff00) >> 8, 41443 (track.samplesize & 0xff), // samplesize 41444 0x00, 0x00, // pre_defined 41445 0x00, 0x00, // reserved 41446 41447 (track.samplerate & 0xff00) >> 8, 41448 (track.samplerate & 0xff), 41449 0x00, 0x00 // samplerate, 16.16 41450 41451 // MP4AudioSampleEntry, ISO/IEC 14496-14 41452 ]), esds(track)); 41453 }; 41454}()); 41455 41456tkhd = function(track) { 41457 var result = new Uint8Array([ 41458 0x00, // version 0 41459 0x00, 0x00, 0x07, // flags 41460 0x00, 0x00, 0x00, 0x00, // creation_time 41461 0x00, 0x00, 0x00, 0x00, // modification_time 41462 (track.id & 0xFF000000) >> 24, 41463 (track.id & 0xFF0000) >> 16, 41464 (track.id & 0xFF00) >> 8, 41465 track.id & 0xFF, // track_ID 41466 0x00, 0x00, 0x00, 0x00, // reserved 41467 (track.duration & 0xFF000000) >> 24, 41468 (track.duration & 0xFF0000) >> 16, 41469 (track.duration & 0xFF00) >> 8, 41470 track.duration & 0xFF, // duration 41471 0x00, 0x00, 0x00, 0x00, 41472 0x00, 0x00, 0x00, 0x00, // reserved 41473 0x00, 0x00, // layer 41474 0x00, 0x00, // alternate_group 41475 0x01, 0x00, // non-audio track volume 41476 0x00, 0x00, // reserved 41477 0x00, 0x01, 0x00, 0x00, 41478 0x00, 0x00, 0x00, 0x00, 41479 0x00, 0x00, 0x00, 0x00, 41480 0x00, 0x00, 0x00, 0x00, 41481 0x00, 0x01, 0x00, 0x00, 41482 0x00, 0x00, 0x00, 0x00, 41483 0x00, 0x00, 0x00, 0x00, 41484 0x00, 0x00, 0x00, 0x00, 41485 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix 41486 (track.width & 0xFF00) >> 8, 41487 track.width & 0xFF, 41488 0x00, 0x00, // width 41489 (track.height & 0xFF00) >> 8, 41490 track.height & 0xFF, 41491 0x00, 0x00 // height 41492 ]); 41493 41494 return box(types.tkhd, result); 41495}; 41496 41497/** 41498 * Generate a track fragment (traf) box. A traf box collects metadata 41499 * about tracks in a movie fragment (moof) box. 41500 */ 41501traf = function(track) { 41502 var trackFragmentHeader, trackFragmentDecodeTime, trackFragmentRun, 41503 sampleDependencyTable, dataOffset, 41504 upperWordBaseMediaDecodeTime, lowerWordBaseMediaDecodeTime; 41505 41506 trackFragmentHeader = box(types.tfhd, new Uint8Array([ 41507 0x00, // version 0 41508 0x00, 0x00, 0x3a, // flags 41509 (track.id & 0xFF000000) >> 24, 41510 (track.id & 0xFF0000) >> 16, 41511 (track.id & 0xFF00) >> 8, 41512 (track.id & 0xFF), // track_ID 41513 0x00, 0x00, 0x00, 0x01, // sample_description_index 41514 0x00, 0x00, 0x00, 0x00, // default_sample_duration 41515 0x00, 0x00, 0x00, 0x00, // default_sample_size 41516 0x00, 0x00, 0x00, 0x00 // default_sample_flags 41517 ])); 41518 41519 upperWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime / (UINT32_MAX + 1)); 41520 lowerWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime % (UINT32_MAX + 1)); 41521 41522 trackFragmentDecodeTime = box(types.tfdt, new Uint8Array([ 41523 0x01, // version 1 41524 0x00, 0x00, 0x00, // flags 41525 // baseMediaDecodeTime 41526 (upperWordBaseMediaDecodeTime >>> 24) & 0xFF, 41527 (upperWordBaseMediaDecodeTime >>> 16) & 0xFF, 41528 (upperWordBaseMediaDecodeTime >>> 8) & 0xFF, 41529 upperWordBaseMediaDecodeTime & 0xFF, 41530 (lowerWordBaseMediaDecodeTime >>> 24) & 0xFF, 41531 (lowerWordBaseMediaDecodeTime >>> 16) & 0xFF, 41532 (lowerWordBaseMediaDecodeTime >>> 8) & 0xFF, 41533 lowerWordBaseMediaDecodeTime & 0xFF 41534 ])); 41535 41536 // the data offset specifies the number of bytes from the start of 41537 // the containing moof to the first payload byte of the associated 41538 // mdat 41539 dataOffset = (32 + // tfhd 41540 20 + // tfdt 41541 8 + // traf header 41542 16 + // mfhd 41543 8 + // moof header 41544 8); // mdat header 41545 41546 // audio tracks require less metadata 41547 if (track.type === 'audio') { 41548 trackFragmentRun = trun(track, dataOffset); 41549 return box(types.traf, 41550 trackFragmentHeader, 41551 trackFragmentDecodeTime, 41552 trackFragmentRun); 41553 } 41554 41555 // video tracks should contain an independent and disposable samples 41556 // box (sdtp) 41557 // generate one and adjust offsets to match 41558 sampleDependencyTable = sdtp(track); 41559 trackFragmentRun = trun(track, 41560 sampleDependencyTable.length + dataOffset); 41561 return box(types.traf, 41562 trackFragmentHeader, 41563 trackFragmentDecodeTime, 41564 trackFragmentRun, 41565 sampleDependencyTable); 41566}; 41567 41568/** 41569 * Generate a track box. 41570 * @param track {object} a track definition 41571 * @return {Uint8Array} the track box 41572 */ 41573trak = function(track) { 41574 track.duration = track.duration || 0xffffffff; 41575 return box(types.trak, 41576 tkhd(track), 41577 mdia(track)); 41578}; 41579 41580trex = function(track) { 41581 var result = new Uint8Array([ 41582 0x00, // version 0 41583 0x00, 0x00, 0x00, // flags 41584 (track.id & 0xFF000000) >> 24, 41585 (track.id & 0xFF0000) >> 16, 41586 (track.id & 0xFF00) >> 8, 41587 (track.id & 0xFF), // track_ID 41588 0x00, 0x00, 0x00, 0x01, // default_sample_description_index 41589 0x00, 0x00, 0x00, 0x00, // default_sample_duration 41590 0x00, 0x00, 0x00, 0x00, // default_sample_size 41591 0x00, 0x01, 0x00, 0x01 // default_sample_flags 41592 ]); 41593 // the last two bytes of default_sample_flags is the sample 41594 // degradation priority, a hint about the importance of this sample 41595 // relative to others. Lower the degradation priority for all sample 41596 // types other than video. 41597 if (track.type !== 'video') { 41598 result[result.length - 1] = 0x00; 41599 } 41600 41601 return box(types.trex, result); 41602}; 41603 41604(function() { 41605 var audioTrun, videoTrun, trunHeader; 41606 41607 // This method assumes all samples are uniform. That is, if a 41608 // duration is present for the first sample, it will be present for 41609 // all subsequent samples. 41610 // see ISO/IEC 14496-12:2012, Section 8.8.8.1 41611 trunHeader = function(samples, offset) { 41612 var durationPresent = 0, sizePresent = 0, 41613 flagsPresent = 0, compositionTimeOffset = 0; 41614 41615 // trun flag constants 41616 if (samples.length) { 41617 if (samples[0].duration !== undefined) { 41618 durationPresent = 0x1; 41619 } 41620 if (samples[0].size !== undefined) { 41621 sizePresent = 0x2; 41622 } 41623 if (samples[0].flags !== undefined) { 41624 flagsPresent = 0x4; 41625 } 41626 if (samples[0].compositionTimeOffset !== undefined) { 41627 compositionTimeOffset = 0x8; 41628 } 41629 } 41630 41631 return [ 41632 0x00, // version 0 41633 0x00, 41634 durationPresent | sizePresent | flagsPresent | compositionTimeOffset, 41635 0x01, // flags 41636 (samples.length & 0xFF000000) >>> 24, 41637 (samples.length & 0xFF0000) >>> 16, 41638 (samples.length & 0xFF00) >>> 8, 41639 samples.length & 0xFF, // sample_count 41640 (offset & 0xFF000000) >>> 24, 41641 (offset & 0xFF0000) >>> 16, 41642 (offset & 0xFF00) >>> 8, 41643 offset & 0xFF // data_offset 41644 ]; 41645 }; 41646 41647 videoTrun = function(track, offset) { 41648 var bytes, samples, sample, i; 41649 41650 samples = track.samples || []; 41651 offset += 8 + 12 + (16 * samples.length); 41652 41653 bytes = trunHeader(samples, offset); 41654 41655 for (i = 0; i < samples.length; i++) { 41656 sample = samples[i]; 41657 bytes = bytes.concat([ 41658 (sample.duration & 0xFF000000) >>> 24, 41659 (sample.duration & 0xFF0000) >>> 16, 41660 (sample.duration & 0xFF00) >>> 8, 41661 sample.duration & 0xFF, // sample_duration 41662 (sample.size & 0xFF000000) >>> 24, 41663 (sample.size & 0xFF0000) >>> 16, 41664 (sample.size & 0xFF00) >>> 8, 41665 sample.size & 0xFF, // sample_size 41666 (sample.flags.isLeading << 2) | sample.flags.dependsOn, 41667 (sample.flags.isDependedOn << 6) | 41668 (sample.flags.hasRedundancy << 4) | 41669 (sample.flags.paddingValue << 1) | 41670 sample.flags.isNonSyncSample, 41671 sample.flags.degradationPriority & 0xF0 << 8, 41672 sample.flags.degradationPriority & 0x0F, // sample_flags 41673 (sample.compositionTimeOffset & 0xFF000000) >>> 24, 41674 (sample.compositionTimeOffset & 0xFF0000) >>> 16, 41675 (sample.compositionTimeOffset & 0xFF00) >>> 8, 41676 sample.compositionTimeOffset & 0xFF // sample_composition_time_offset 41677 ]); 41678 } 41679 return box(types.trun, new Uint8Array(bytes)); 41680 }; 41681 41682 audioTrun = function(track, offset) { 41683 var bytes, samples, sample, i; 41684 41685 samples = track.samples || []; 41686 offset += 8 + 12 + (8 * samples.length); 41687 41688 bytes = trunHeader(samples, offset); 41689 41690 for (i = 0; i < samples.length; i++) { 41691 sample = samples[i]; 41692 bytes = bytes.concat([ 41693 (sample.duration & 0xFF000000) >>> 24, 41694 (sample.duration & 0xFF0000) >>> 16, 41695 (sample.duration & 0xFF00) >>> 8, 41696 sample.duration & 0xFF, // sample_duration 41697 (sample.size & 0xFF000000) >>> 24, 41698 (sample.size & 0xFF0000) >>> 16, 41699 (sample.size & 0xFF00) >>> 8, 41700 sample.size & 0xFF]); // sample_size 41701 } 41702 41703 return box(types.trun, new Uint8Array(bytes)); 41704 }; 41705 41706 trun = function(track, offset) { 41707 if (track.type === 'audio') { 41708 return audioTrun(track, offset); 41709 } 41710 41711 return videoTrun(track, offset); 41712 }; 41713}()); 41714 41715module.exports = { 41716 ftyp: ftyp, 41717 mdat: mdat, 41718 moof: moof, 41719 moov: moov, 41720 initSegment: function(tracks) { 41721 var 41722 fileType = ftyp(), 41723 movie = moov(tracks), 41724 result; 41725 41726 result = new Uint8Array(fileType.byteLength + movie.byteLength); 41727 result.set(fileType); 41728 result.set(movie, fileType.byteLength); 41729 return result; 41730 } 41731}; 41732 41733},{}],57:[function(require,module,exports){ 41734/** 41735 * mux.js 41736 * 41737 * Copyright (c) 2015 Brightcove 41738 * All rights reserved. 41739 * 41740 * Utilities to detect basic properties and metadata about MP4s. 41741 */ 41742'use strict'; 41743 41744var findBox, parseType, timescale, startTime; 41745 41746// Find the data for a box specified by its path 41747findBox = function(data, path) { 41748 var results = [], 41749 i, size, type, end, subresults; 41750 41751 if (!path.length) { 41752 // short-circuit the search for empty paths 41753 return null; 41754 } 41755 41756 for (i = 0; i < data.byteLength;) { 41757 size = data[i] << 24; 41758 size |= data[i + 1] << 16; 41759 size |= data[i + 2] << 8; 41760 size |= data[i + 3]; 41761 41762 type = parseType(data.subarray(i + 4, i + 8)); 41763 41764 end = size > 1 ? i + size : data.byteLength; 41765 41766 if (type === path[0]) { 41767 if (path.length === 1) { 41768 // this is the end of the path and we've found the box we were 41769 // looking for 41770 results.push(data.subarray(i + 8, end)); 41771 } else { 41772 // recursively search for the next box along the path 41773 subresults = findBox(data.subarray(i + 8, end), path.slice(1)); 41774 if (subresults.length) { 41775 results = results.concat(subresults); 41776 } 41777 } 41778 } 41779 i = end; 41780 } 41781 41782 // we've finished searching all of data 41783 return results; 41784}; 41785 41786/** 41787 * Returns the string representation of an ASCII encoded four byte buffer. 41788 * @param buffer {Uint8Array} a four-byte buffer to translate 41789 * @return {string} the corresponding string 41790 */ 41791parseType = function(buffer) { 41792 var result = ''; 41793 result += String.fromCharCode(buffer[0]); 41794 result += String.fromCharCode(buffer[1]); 41795 result += String.fromCharCode(buffer[2]); 41796 result += String.fromCharCode(buffer[3]); 41797 return result; 41798}; 41799 41800/** 41801 * Parses an MP4 initialization segment and extracts the timescale 41802 * values for any declared tracks. Timescale values indicate the 41803 * number of clock ticks per second to assume for time-based values 41804 * elsewhere in the MP4. 41805 * 41806 * To determine the start time of an MP4, you need two pieces of 41807 * information: the timescale unit and the earliest base media decode 41808 * time. Multiple timescales can be specified within an MP4 but the 41809 * base media decode time is always expressed in the timescale from 41810 * the media header box for the track: 41811 * ``` 41812 * moov > trak > mdia > mdhd.timescale 41813 * ``` 41814 * @param init {Uint8Array} the bytes of the init segment 41815 * @return {object} a hash of track ids to timescale values or null if 41816 * the init segment is malformed. 41817 */ 41818timescale = function(init) { 41819 var 41820 result = {}, 41821 traks = findBox(init, ['moov', 'trak']); 41822 41823 // mdhd timescale 41824 return traks.reduce(function(result, trak) { 41825 var tkhd, version, index, id, mdhd; 41826 41827 tkhd = findBox(trak, ['tkhd'])[0]; 41828 if (!tkhd) { 41829 return null; 41830 } 41831 version = tkhd[0]; 41832 index = version === 0 ? 12 : 20; 41833 id = tkhd[index] << 24 | 41834 tkhd[index + 1] << 16 | 41835 tkhd[index + 2] << 8 | 41836 tkhd[index + 3]; 41837 41838 mdhd = findBox(trak, ['mdia', 'mdhd'])[0]; 41839 if (!mdhd) { 41840 return null; 41841 } 41842 version = mdhd[0]; 41843 index = version === 0 ? 12 : 20; 41844 result[id] = mdhd[index] << 24 | 41845 mdhd[index + 1] << 16 | 41846 mdhd[index + 2] << 8 | 41847 mdhd[index + 3]; 41848 return result; 41849 }, result); 41850}; 41851 41852/** 41853 * Determine the base media decode start time, in seconds, for an MP4 41854 * fragment. If multiple fragments are specified, the earliest time is 41855 * returned. 41856 * 41857 * The base media decode time can be parsed from track fragment 41858 * metadata: 41859 * ``` 41860 * moof > traf > tfdt.baseMediaDecodeTime 41861 * ``` 41862 * It requires the timescale value from the mdhd to interpret. 41863 * 41864 * @param timescale {object} a hash of track ids to timescale values. 41865 * @return {number} the earliest base media decode start time for the 41866 * fragment, in seconds 41867 */ 41868startTime = function(timescale, fragment) { 41869 var trafs, baseTimes, result; 41870 41871 // we need info from two childrend of each track fragment box 41872 trafs = findBox(fragment, ['moof', 'traf']); 41873 41874 // determine the start times for each track 41875 baseTimes = [].concat.apply([], trafs.map(function(traf) { 41876 return findBox(traf, ['tfhd']).map(function(tfhd) { 41877 var id, scale, baseTime; 41878 41879 // get the track id from the tfhd 41880 id = tfhd[4] << 24 | 41881 tfhd[5] << 16 | 41882 tfhd[6] << 8 | 41883 tfhd[7]; 41884 // assume a 90kHz clock if no timescale was specified 41885 scale = timescale[id] || 90e3; 41886 41887 // get the base media decode time from the tfdt 41888 baseTime = findBox(traf, ['tfdt']).map(function(tfdt) { 41889 var version, result; 41890 41891 version = tfdt[0]; 41892 result = tfdt[4] << 24 | 41893 tfdt[5] << 16 | 41894 tfdt[6] << 8 | 41895 tfdt[7]; 41896 if (version === 1) { 41897 result *= Math.pow(2, 32); 41898 result += tfdt[8] << 24 | 41899 tfdt[9] << 16 | 41900 tfdt[10] << 8 | 41901 tfdt[11]; 41902 } 41903 return result; 41904 })[0]; 41905 baseTime = baseTime || Infinity; 41906 41907 // convert base time to seconds 41908 return baseTime / scale; 41909 }); 41910 })); 41911 41912 // return the minimum 41913 result = Math.min.apply(null, baseTimes); 41914 return isFinite(result) ? result : 0; 41915}; 41916 41917module.exports = { 41918 parseType: parseType, 41919 timescale: timescale, 41920 startTime: startTime 41921}; 41922 41923},{}],58:[function(require,module,exports){ 41924/** 41925 * mux.js 41926 * 41927 * Copyright (c) 2015 Brightcove 41928 * All rights reserved. 41929 * 41930 * A stream-based mp2t to mp4 converter. This utility can be used to 41931 * deliver mp4s to a SourceBuffer on platforms that support native 41932 * Media Source Extensions. 41933 */ 41934'use strict'; 41935 41936var Stream = require('../utils/stream.js'); 41937var mp4 = require('./mp4-generator.js'); 41938var m2ts = require('../m2ts/m2ts.js'); 41939var AdtsStream = require('../codecs/adts.js'); 41940var H264Stream = require('../codecs/h264').H264Stream; 41941var AacStream = require('../aac'); 41942var coneOfSilence = require('../data/silence'); 41943var clock = require('../utils/clock'); 41944 41945// constants 41946var AUDIO_PROPERTIES = [ 41947 'audioobjecttype', 41948 'channelcount', 41949 'samplerate', 41950 'samplingfrequencyindex', 41951 'samplesize' 41952]; 41953 41954var VIDEO_PROPERTIES = [ 41955 'width', 41956 'height', 41957 'profileIdc', 41958 'levelIdc', 41959 'profileCompatibility' 41960]; 41961 41962var ONE_SECOND_IN_TS = 90000; // 90kHz clock 41963 41964// object types 41965var VideoSegmentStream, AudioSegmentStream, Transmuxer, CoalesceStream; 41966 41967// Helper functions 41968var 41969 createDefaultSample, 41970 isLikelyAacData, 41971 collectDtsInfo, 41972 clearDtsInfo, 41973 calculateTrackBaseMediaDecodeTime, 41974 arrayEquals, 41975 sumFrameByteLengths; 41976 41977/** 41978 * Default sample object 41979 * see ISO/IEC 14496-12:2012, section 8.6.4.3 41980 */ 41981createDefaultSample = function() { 41982 return { 41983 size: 0, 41984 flags: { 41985 isLeading: 0, 41986 dependsOn: 1, 41987 isDependedOn: 0, 41988 hasRedundancy: 0, 41989 degradationPriority: 0 41990 } 41991 }; 41992}; 41993 41994isLikelyAacData = function(data) { 41995 if ((data[0] === 'I'.charCodeAt(0)) && 41996 (data[1] === 'D'.charCodeAt(0)) && 41997 (data[2] === '3'.charCodeAt(0))) { 41998 return true; 41999 } 42000 return false; 42001}; 42002 42003/** 42004 * Compare two arrays (even typed) for same-ness 42005 */ 42006arrayEquals = function(a, b) { 42007 var 42008 i; 42009 42010 if (a.length !== b.length) { 42011 return false; 42012 } 42013 42014 // compare the value of each element in the array 42015 for (i = 0; i < a.length; i++) { 42016 if (a[i] !== b[i]) { 42017 return false; 42018 } 42019 } 42020 42021 return true; 42022}; 42023 42024/** 42025 * Sum the `byteLength` properties of the data in each AAC frame 42026 */ 42027sumFrameByteLengths = function(array) { 42028 var 42029 i, 42030 currentObj, 42031 sum = 0; 42032 42033 // sum the byteLength's all each nal unit in the frame 42034 for (i = 0; i < array.length; i++) { 42035 currentObj = array[i]; 42036 sum += currentObj.data.byteLength; 42037 } 42038 42039 return sum; 42040}; 42041 42042/** 42043 * Constructs a single-track, ISO BMFF media segment from AAC data 42044 * events. The output of this stream can be fed to a SourceBuffer 42045 * configured with a suitable initialization segment. 42046 */ 42047AudioSegmentStream = function(track) { 42048 var 42049 adtsFrames = [], 42050 sequenceNumber = 0, 42051 earliestAllowedDts = 0, 42052 audioAppendStartTs = 0, 42053 videoBaseMediaDecodeTime = Infinity; 42054 42055 AudioSegmentStream.prototype.init.call(this); 42056 42057 this.push = function(data) { 42058 collectDtsInfo(track, data); 42059 42060 if (track) { 42061 AUDIO_PROPERTIES.forEach(function(prop) { 42062 track[prop] = data[prop]; 42063 }); 42064 } 42065 42066 // buffer audio data until end() is called 42067 adtsFrames.push(data); 42068 }; 42069 42070 this.setEarliestDts = function(earliestDts) { 42071 earliestAllowedDts = earliestDts - track.timelineStartInfo.baseMediaDecodeTime; 42072 }; 42073 42074 this.setVideoBaseMediaDecodeTime = function(baseMediaDecodeTime) { 42075 videoBaseMediaDecodeTime = baseMediaDecodeTime; 42076 }; 42077 42078 this.setAudioAppendStart = function(timestamp) { 42079 audioAppendStartTs = timestamp; 42080 }; 42081 42082 this.flush = function() { 42083 var 42084 frames, 42085 moof, 42086 mdat, 42087 boxes; 42088 42089 // return early if no audio data has been observed 42090 if (adtsFrames.length === 0) { 42091 this.trigger('done', 'AudioSegmentStream'); 42092 return; 42093 } 42094 42095 frames = this.trimAdtsFramesByEarliestDts_(adtsFrames); 42096 track.baseMediaDecodeTime = calculateTrackBaseMediaDecodeTime(track); 42097 42098 this.prefixWithSilence_(track, frames); 42099 42100 // we have to build the index from byte locations to 42101 // samples (that is, adts frames) in the audio data 42102 track.samples = this.generateSampleTable_(frames); 42103 42104 // concatenate the audio data to constuct the mdat 42105 mdat = mp4.mdat(this.concatenateFrameData_(frames)); 42106 42107 adtsFrames = []; 42108 42109 moof = mp4.moof(sequenceNumber, [track]); 42110 boxes = new Uint8Array(moof.byteLength + mdat.byteLength); 42111 42112 // bump the sequence number for next time 42113 sequenceNumber++; 42114 42115 boxes.set(moof); 42116 boxes.set(mdat, moof.byteLength); 42117 42118 clearDtsInfo(track); 42119 42120 this.trigger('data', {track: track, boxes: boxes}); 42121 this.trigger('done', 'AudioSegmentStream'); 42122 }; 42123 42124 // Possibly pad (prefix) the audio track with silence if appending this track 42125 // would lead to the introduction of a gap in the audio buffer 42126 this.prefixWithSilence_ = function(track, frames) { 42127 var 42128 baseMediaDecodeTimeTs, 42129 frameDuration = 0, 42130 audioGapDuration = 0, 42131 audioFillFrameCount = 0, 42132 audioFillDuration = 0, 42133 silentFrame, 42134 i; 42135 42136 if (!frames.length) { 42137 return; 42138 } 42139 42140 baseMediaDecodeTimeTs = clock.audioTsToVideoTs(track.baseMediaDecodeTime, track.samplerate); 42141 // determine frame clock duration based on sample rate, round up to avoid overfills 42142 frameDuration = Math.ceil(ONE_SECOND_IN_TS / (track.samplerate / 1024)); 42143 42144 if (audioAppendStartTs && videoBaseMediaDecodeTime) { 42145 // insert the shortest possible amount (audio gap or audio to video gap) 42146 audioGapDuration = 42147 baseMediaDecodeTimeTs - Math.max(audioAppendStartTs, videoBaseMediaDecodeTime); 42148 // number of full frames in the audio gap 42149 audioFillFrameCount = Math.floor(audioGapDuration / frameDuration); 42150 audioFillDuration = audioFillFrameCount * frameDuration; 42151 } 42152 42153 // don't attempt to fill gaps smaller than a single frame or larger 42154 // than a half second 42155 if (audioFillFrameCount < 1 || audioFillDuration > ONE_SECOND_IN_TS / 2) { 42156 return; 42157 } 42158 42159 silentFrame = coneOfSilence[track.samplerate]; 42160 42161 if (!silentFrame) { 42162 // we don't have a silent frame pregenerated for the sample rate, so use a frame 42163 // from the content instead 42164 silentFrame = frames[0].data; 42165 } 42166 42167 for (i = 0; i < audioFillFrameCount; i++) { 42168 frames.splice(i, 0, { 42169 data: silentFrame 42170 }); 42171 } 42172 42173 track.baseMediaDecodeTime -= 42174 Math.floor(clock.videoTsToAudioTs(audioFillDuration, track.samplerate)); 42175 }; 42176 42177 // If the audio segment extends before the earliest allowed dts 42178 // value, remove AAC frames until starts at or after the earliest 42179 // allowed DTS so that we don't end up with a negative baseMedia- 42180 // DecodeTime for the audio track 42181 this.trimAdtsFramesByEarliestDts_ = function(adtsFrames) { 42182 if (track.minSegmentDts >= earliestAllowedDts) { 42183 return adtsFrames; 42184 } 42185 42186 // We will need to recalculate the earliest segment Dts 42187 track.minSegmentDts = Infinity; 42188 42189 return adtsFrames.filter(function(currentFrame) { 42190 // If this is an allowed frame, keep it and record it's Dts 42191 if (currentFrame.dts >= earliestAllowedDts) { 42192 track.minSegmentDts = Math.min(track.minSegmentDts, currentFrame.dts); 42193 track.minSegmentPts = track.minSegmentDts; 42194 return true; 42195 } 42196 // Otherwise, discard it 42197 return false; 42198 }); 42199 }; 42200 42201 // generate the track's raw mdat data from an array of frames 42202 this.generateSampleTable_ = function(frames) { 42203 var 42204 i, 42205 currentFrame, 42206 samples = []; 42207 42208 for (i = 0; i < frames.length; i++) { 42209 currentFrame = frames[i]; 42210 samples.push({ 42211 size: currentFrame.data.byteLength, 42212 duration: 1024 // For AAC audio, all samples contain 1024 samples 42213 }); 42214 } 42215 return samples; 42216 }; 42217 42218 // generate the track's sample table from an array of frames 42219 this.concatenateFrameData_ = function(frames) { 42220 var 42221 i, 42222 currentFrame, 42223 dataOffset = 0, 42224 data = new Uint8Array(sumFrameByteLengths(frames)); 42225 42226 for (i = 0; i < frames.length; i++) { 42227 currentFrame = frames[i]; 42228 42229 data.set(currentFrame.data, dataOffset); 42230 dataOffset += currentFrame.data.byteLength; 42231 } 42232 return data; 42233 }; 42234}; 42235 42236AudioSegmentStream.prototype = new Stream(); 42237 42238/** 42239 * Constructs a single-track, ISO BMFF media segment from H264 data 42240 * events. The output of this stream can be fed to a SourceBuffer 42241 * configured with a suitable initialization segment. 42242 * @param track {object} track metadata configuration 42243 *
vendor: 631 bytes, lines 42243-42271
42243/ 42244VideoSegmentStream = function(track) { 42245 var 42246 sequenceNumber = 0, 42247 nalUnits = [], 42248 config, 42249 pps; 42250 42251 VideoSegmentStream.prototype.init.call(this); 42252 42253 delete track.minPTS; 42254 42255 this.gopCache_ = []; 42256 42257 this.push = function(nalUnit) { 42258 collectDtsInfo(track, nalUnit); 42259 42260 // record the track config 42261 if (nalUnit.nalUnitType === 'seq_parameter_set_rbsp' && !config) { 42262 config = nalUnit.config; 42263 track.sps = [nalUnit.data]; 42264 42265 VIDEO_PROPERTIES.forEach(function(prop) { 42266 track[prop] = config[prop]; 42267 }, this); 42268 } 42269 42270 if (nalUnit.nalUnitType === 'pic_parameter_set_rbsp' && 42271 !pps) {
vendor: 51,100 bytes, lines 42272-43936
42272 pps = nalUnit.data; 42273 track.pps = [nalUnit.data]; 42274 } 42275 42276 // buffer video until flush() is called 42277 nalUnits.push(nalUnit); 42278 }; 42279 42280 this.flush = function() { 42281 var 42282 frames, 42283 gopForFusion, 42284 gops, 42285 moof, 42286 mdat, 42287 boxes; 42288 42289 // Throw away nalUnits at the start of the byte stream until 42290 // we find the first AUD 42291 while (nalUnits.length) { 42292 if (nalUnits[0].nalUnitType === 'access_unit_delimiter_rbsp') { 42293 break; 42294 } 42295 nalUnits.shift(); 42296 } 42297 42298 // Return early if no video data has been observed 42299 if (nalUnits.length === 0) { 42300 this.resetStream_(); 42301 this.trigger('done', 'VideoSegmentStream'); 42302 return; 42303 } 42304 42305 // Organize the raw nal-units into arrays that represent 42306 // higher-level constructs such as frames and gops 42307 // (group-of-pictures) 42308 frames = this.groupNalsIntoFrames_(nalUnits); 42309 gops = this.groupFramesIntoGops_(frames); 42310 42311 // If the first frame of this fragment is not a keyframe we have 42312 // a problem since MSE (on Chrome) requires a leading keyframe. 42313 // 42314 // We have two approaches to repairing this situation: 42315 // 1) GOP-FUSION: 42316 // This is where we keep track of the GOPS (group-of-pictures) 42317 // from previous fragments and attempt to find one that we can 42318 // prepend to the current fragment in order to create a valid 42319 // fragment. 42320 // 2) KEYFRAME-PULLING: 42321 // Here we search for the first keyframe in the fragment and 42322 // throw away all the frames between the start of the fragment 42323 // and that keyframe. We then extend the duration and pull the 42324 // PTS of the keyframe forward so that it covers the time range 42325 // of the frames that were disposed of. 42326 // 42327 // #1 is far prefereable over #2 which can cause "stuttering" but 42328 // requires more things to be just right. 42329 if (!gops[0][0].keyFrame) { 42330 // Search for a gop for fusion from our gopCache 42331 gopForFusion = this.getGopForFusion_(nalUnits[0], track); 42332 42333 if (gopForFusion) { 42334 gops.unshift(gopForFusion); 42335 // Adjust Gops' metadata to account for the inclusion of the 42336 // new gop at the beginning 42337 gops.byteLength += gopForFusion.byteLength; 42338 gops.nalCount += gopForFusion.nalCount; 42339 gops.pts = gopForFusion.pts; 42340 gops.dts = gopForFusion.dts; 42341 gops.duration += gopForFusion.duration; 42342 } else { 42343 // If we didn't find a candidate gop fall back to keyrame-pulling 42344 gops = this.extendFirstKeyFrame_(gops); 42345 } 42346 } 42347 collectDtsInfo(track, gops); 42348 42349 // First, we have to build the index from byte locations to 42350 // samples (that is, frames) in the video data 42351 track.samples = this.generateSampleTable_(gops); 42352 42353 // Concatenate the video data and construct the mdat 42354 mdat = mp4.mdat(this.concatenateNalData_(gops)); 42355 42356 // save all the nals in the last GOP into the gop cache 42357 this.gopCache_.unshift({ 42358 gop: gops.pop(), 42359 pps: track.pps, 42360 sps: track.sps 42361 }); 42362 42363 // Keep a maximum of 6 GOPs in the cache 42364 this.gopCache_.length = Math.min(6, this.gopCache_.length); 42365 42366 // Clear nalUnits 42367 nalUnits = []; 42368 42369 track.baseMediaDecodeTime = calculateTrackBaseMediaDecodeTime(track); 42370 42371 this.trigger('baseMediaDecodeTime', track.baseMediaDecodeTime); 42372 this.trigger('timelineStartInfo', track.timelineStartInfo); 42373 42374 moof = mp4.moof(sequenceNumber, [track]); 42375 42376 // it would be great to allocate this array up front instead of 42377 // throwing away hundreds of media segment fragments 42378 boxes = new Uint8Array(moof.byteLength + mdat.byteLength); 42379 42380 // Bump the sequence number for next time 42381 sequenceNumber++; 42382 42383 boxes.set(moof); 42384 boxes.set(mdat, moof.byteLength); 42385 42386 this.trigger('data', {track: track, boxes: boxes}); 42387 42388 this.resetStream_(); 42389 42390 // Continue with the flush process now 42391 this.trigger('done', 'VideoSegmentStream'); 42392 }; 42393 42394 this.resetStream_ = function() { 42395 clearDtsInfo(track); 42396 42397 // reset config and pps because they may differ across segments 42398 // for instance, when we are rendition switching 42399 config = undefined; 42400 pps = undefined; 42401 }; 42402 42403 // Search for a candidate Gop for gop-fusion from the gop cache and 42404 // return it or return null if no good candidate was found 42405 this.getGopForFusion_ = function(nalUnit) { 42406 var 42407 halfSecond = 45000, // Half-a-second in a 90khz clock 42408 allowableOverlap = 10000, // About 3 frames @ 30fps 42409 nearestDistance = Infinity, 42410 dtsDistance, 42411 nearestGopObj, 42412 currentGop, 42413 currentGopObj, 42414 i; 42415 42416 // Search for the GOP nearest to the beginning of this nal unit 42417 for (i = 0; i < this.gopCache_.length; i++) { 42418 currentGopObj = this.gopCache_[i]; 42419 currentGop = currentGopObj.gop; 42420 42421 // Reject Gops with different SPS or PPS 42422 if (!(track.pps && arrayEquals(track.pps[0], currentGopObj.pps[0])) || 42423 !(track.sps && arrayEquals(track.sps[0], currentGopObj.sps[0]))) { 42424 continue; 42425 } 42426 42427 // Reject Gops that would require a negative baseMediaDecodeTime 42428 if (currentGop.dts < track.timelineStartInfo.dts) { 42429 continue; 42430 } 42431 42432 // The distance between the end of the gop and the start of the nalUnit 42433 dtsDistance = (nalUnit.dts - currentGop.dts) - currentGop.duration; 42434 42435 // Only consider GOPS that start before the nal unit and end within 42436 // a half-second of the nal unit 42437 if (dtsDistance >= -allowableOverlap && 42438 dtsDistance <= halfSecond) { 42439 42440 // Always use the closest GOP we found if there is more than 42441 // one candidate 42442 if (!nearestGopObj || 42443 nearestDistance > dtsDistance) { 42444 nearestGopObj = currentGopObj; 42445 nearestDistance = dtsDistance; 42446 } 42447 } 42448 } 42449 42450 if (nearestGopObj) { 42451 return nearestGopObj.gop; 42452 } 42453 return null; 42454 }; 42455 42456 this.extendFirstKeyFrame_ = function(gops) { 42457 var currentGop; 42458 42459 if (!gops[0][0].keyFrame && gops.length > 1) { 42460 // Remove the first GOP 42461 currentGop = gops.shift(); 42462 42463 gops.byteLength -= currentGop.byteLength; 42464 gops.nalCount -= currentGop.nalCount; 42465 42466 // Extend the first frame of what is now the 42467 // first gop to cover the time period of the 42468 // frames we just removed 42469 gops[0][0].dts = currentGop.dts; 42470 gops[0][0].pts = currentGop.pts; 42471 gops[0][0].duration += currentGop.duration; 42472 } 42473 42474 return gops; 42475 }; 42476 42477 // Convert an array of nal units into an array of frames with each frame being 42478 // composed of the nal units that make up that frame 42479 // Also keep track of cummulative data about the frame from the nal units such 42480 // as the frame duration, starting pts, etc. 42481 this.groupNalsIntoFrames_ = function(nalUnits) { 42482 var 42483 i, 42484 currentNal, 42485 currentFrame = [], 42486 frames = []; 42487 42488 currentFrame.byteLength = 0; 42489 42490 for (i = 0; i < nalUnits.length; i++) { 42491 currentNal = nalUnits[i]; 42492 42493 // Split on 'aud'-type nal units 42494 if (currentNal.nalUnitType === 'access_unit_delimiter_rbsp') { 42495 // Since the very first nal unit is expected to be an AUD 42496 // only push to the frames array when currentFrame is not empty 42497 if (currentFrame.length) { 42498 currentFrame.duration = currentNal.dts - currentFrame.dts; 42499 frames.push(currentFrame); 42500 } 42501 currentFrame = [currentNal]; 42502 currentFrame.byteLength = currentNal.data.byteLength; 42503 currentFrame.pts = currentNal.pts; 42504 currentFrame.dts = currentNal.dts; 42505 } else { 42506 // Specifically flag key frames for ease of use later 42507 if (currentNal.nalUnitType === 'slice_layer_without_partitioning_rbsp_idr') { 42508 currentFrame.keyFrame = true; 42509 } 42510 currentFrame.duration = currentNal.dts - currentFrame.dts; 42511 currentFrame.byteLength += currentNal.data.byteLength; 42512 currentFrame.push(currentNal); 42513 } 42514 } 42515 42516 // For the last frame, use the duration of the previous frame if we 42517 // have nothing better to go on 42518 if (frames.length && 42519 (!currentFrame.duration || 42520 currentFrame.duration <= 0)) { 42521 currentFrame.duration = frames[frames.length - 1].duration; 42522 } 42523 42524 // Push the final frame 42525 frames.push(currentFrame); 42526 return frames; 42527 }; 42528 42529 // Convert an array of frames into an array of Gop with each Gop being composed 42530 // of the frames that make up that Gop 42531 // Also keep track of cummulative data about the Gop from the frames such as the 42532 // Gop duration, starting pts, etc. 42533 this.groupFramesIntoGops_ = function(frames) { 42534 var 42535 i, 42536 currentFrame, 42537 currentGop = [], 42538 gops = []; 42539 42540 // We must pre-set some of the values on the Gop since we 42541 // keep running totals of these values 42542 currentGop.byteLength = 0; 42543 currentGop.nalCount = 0; 42544 currentGop.duration = 0; 42545 currentGop.pts = frames[0].pts; 42546 currentGop.dts = frames[0].dts; 42547 42548 // store some metadata about all the Gops 42549 gops.byteLength = 0; 42550 gops.nalCount = 0; 42551 gops.duration = 0; 42552 gops.pts = frames[0].pts; 42553 gops.dts = frames[0].dts; 42554 42555 for (i = 0; i < frames.length; i++) { 42556 currentFrame = frames[i]; 42557 42558 if (currentFrame.keyFrame) { 42559 // Since the very first frame is expected to be an keyframe 42560 // only push to the gops array when currentGop is not empty 42561 if (currentGop.length) { 42562 gops.push(currentGop); 42563 gops.byteLength += currentGop.byteLength; 42564 gops.nalCount += currentGop.nalCount; 42565 gops.duration += currentGop.duration; 42566 } 42567 42568 currentGop = [currentFrame]; 42569 currentGop.nalCount = currentFrame.length; 42570 currentGop.byteLength = currentFrame.byteLength; 42571 currentGop.pts = currentFrame.pts; 42572 currentGop.dts = currentFrame.dts; 42573 currentGop.duration = currentFrame.duration; 42574 } else { 42575 currentGop.duration += currentFrame.duration; 42576 currentGop.nalCount += currentFrame.length; 42577 currentGop.byteLength += currentFrame.byteLength; 42578 currentGop.push(currentFrame); 42579 } 42580 } 42581 42582 if (gops.length && currentGop.duration <= 0) { 42583 currentGop.duration = gops[gops.length - 1].duration; 42584 } 42585 gops.byteLength += currentGop.byteLength; 42586 gops.nalCount += currentGop.nalCount; 42587 gops.duration += currentGop.duration; 42588 42589 // push the final Gop 42590 gops.push(currentGop); 42591 return gops; 42592 }; 42593 42594 // generate the track's sample table from an array of gops 42595 this.generateSampleTable_ = function(gops, baseDataOffset) { 42596 var 42597 h, i, 42598 sample, 42599 currentGop, 42600 currentFrame, 42601 dataOffset = baseDataOffset || 0, 42602 samples = []; 42603 42604 for (h = 0; h < gops.length; h++) { 42605 currentGop = gops[h]; 42606 42607 for (i = 0; i < currentGop.length; i++) { 42608 currentFrame = currentGop[i]; 42609 42610 sample = createDefaultSample(); 42611 42612 sample.dataOffset = dataOffset; 42613 sample.compositionTimeOffset = currentFrame.pts - currentFrame.dts; 42614 sample.duration = currentFrame.duration; 42615 sample.size = 4 * currentFrame.length; // Space for nal unit size 42616 sample.size += currentFrame.byteLength; 42617 42618 if (currentFrame.keyFrame) { 42619 sample.flags.dependsOn = 2; 42620 } 42621 42622 dataOffset += sample.size; 42623 42624 samples.push(sample); 42625 } 42626 } 42627 return samples; 42628 }; 42629 42630 // generate the track's raw mdat data from an array of gops 42631 this.concatenateNalData_ = function(gops) { 42632 var 42633 h, i, j, 42634 currentGop, 42635 currentFrame, 42636 currentNal, 42637 dataOffset = 0, 42638 nalsByteLength = gops.byteLength, 42639 numberOfNals = gops.nalCount, 42640 totalByteLength = nalsByteLength + 4 * numberOfNals, 42641 data = new Uint8Array(totalByteLength), 42642 view = new DataView(data.buffer); 42643 42644 // For each Gop.. 42645 for (h = 0; h < gops.length; h++) { 42646 currentGop = gops[h]; 42647 42648 // For each Frame.. 42649 for (i = 0; i < currentGop.length; i++) { 42650 currentFrame = currentGop[i]; 42651 42652 // For each NAL.. 42653 for (j = 0; j < currentFrame.length; j++) { 42654 currentNal = currentFrame[j]; 42655 42656 view.setUint32(dataOffset, currentNal.data.byteLength); 42657 dataOffset += 4; 42658 data.set(currentNal.data, dataOffset); 42659 dataOffset += currentNal.data.byteLength; 42660 } 42661 } 42662 } 42663 return data; 42664 }; 42665}; 42666 42667VideoSegmentStream.prototype = new Stream(); 42668 42669/** 42670 * Store information about the start and end of the track and the 42671 * duration for each frame/sample we process in order to calculate 42672 * the baseMediaDecodeTime 42673 */ 42674collectDtsInfo = function(track, data) { 42675 if (typeof data.pts === 'number') { 42676 if (track.timelineStartInfo.pts === undefined) { 42677 track.timelineStartInfo.pts = data.pts; 42678 } 42679 42680 if (track.minSegmentPts === undefined) { 42681 track.minSegmentPts = data.pts; 42682 } else { 42683 track.minSegmentPts = Math.min(track.minSegmentPts, data.pts); 42684 } 42685 42686 if (track.maxSegmentPts === undefined) { 42687 track.maxSegmentPts = data.pts; 42688 } else { 42689 track.maxSegmentPts = Math.max(track.maxSegmentPts, data.pts); 42690 } 42691 } 42692 42693 if (typeof data.dts === 'number') { 42694 if (track.timelineStartInfo.dts === undefined) { 42695 track.timelineStartInfo.dts = data.dts; 42696 } 42697 42698 if (track.minSegmentDts === undefined) { 42699 track.minSegmentDts = data.dts; 42700 } else { 42701 track.minSegmentDts = Math.min(track.minSegmentDts, data.dts); 42702 } 42703 42704 if (track.maxSegmentDts === undefined) { 42705 track.maxSegmentDts = data.dts; 42706 } else { 42707 track.maxSegmentDts = Math.max(track.maxSegmentDts, data.dts); 42708 } 42709 } 42710}; 42711 42712/** 42713 * Clear values used to calculate the baseMediaDecodeTime between 42714 * tracks 42715 */ 42716clearDtsInfo = function(track) { 42717 delete track.minSegmentDts; 42718 delete track.maxSegmentDts; 42719 delete track.minSegmentPts; 42720 delete track.maxSegmentPts; 42721}; 42722 42723/** 42724 * Calculate the track's baseMediaDecodeTime based on the earliest 42725 * DTS the transmuxer has ever seen and the minimum DTS for the 42726 * current track 42727 */ 42728calculateTrackBaseMediaDecodeTime = function(track) { 42729 var 42730 baseMediaDecodeTime, 42731 scale, 42732 // Calculate the distance, in time, that this segment starts from the start 42733 // of the timeline (earliest time seen since the transmuxer initialized) 42734 timeSinceStartOfTimeline = track.minSegmentDts - track.timelineStartInfo.dts; 42735 42736 // track.timelineStartInfo.baseMediaDecodeTime is the location, in time, where 42737 // we want the start of the first segment to be placed 42738 baseMediaDecodeTime = track.timelineStartInfo.baseMediaDecodeTime; 42739 42740 // Add to that the distance this segment is from the very first 42741 baseMediaDecodeTime += timeSinceStartOfTimeline; 42742 42743 // baseMediaDecodeTime must not become negative 42744 baseMediaDecodeTime = Math.max(0, baseMediaDecodeTime); 42745 42746 if (track.type === 'audio') { 42747 // Audio has a different clock equal to the sampling_rate so we need to 42748 // scale the PTS values into the clock rate of the track 42749 scale = track.samplerate / ONE_SECOND_IN_TS; 42750 baseMediaDecodeTime *= scale; 42751 baseMediaDecodeTime = Math.floor(baseMediaDecodeTime); 42752 } 42753 42754 return baseMediaDecodeTime; 42755}; 42756 42757/** 42758 * A Stream that can combine multiple streams (ie. audio & video) 42759 * into a single output segment for MSE. Also supports audio-only 42760 * and video-only streams. 42761 */ 42762CoalesceStream = function(options, metadataStream) { 42763 // Number of Tracks per output segment 42764 // If greater than 1, we combine multiple 42765 // tracks into a single segment 42766 this.numberOfTracks = 0; 42767 this.metadataStream = metadataStream; 42768 42769 if (typeof options.remux !== 'undefined') { 42770 this.remuxTracks = !!options.remux; 42771 } else { 42772 this.remuxTracks = true; 42773 } 42774 42775 this.pendingTracks = []; 42776 this.videoTrack = null; 42777 this.pendingBoxes = []; 42778 this.pendingCaptions = []; 42779 this.pendingMetadata = []; 42780 this.pendingBytes = 0; 42781 this.emittedTracks = 0; 42782 42783 CoalesceStream.prototype.init.call(this); 42784 42785 // Take output from multiple 42786 this.push = function(output) { 42787 // buffer incoming captions until the associated video segment 42788 // finishes 42789 if (output.text) { 42790 return this.pendingCaptions.push(output); 42791 } 42792 // buffer incoming id3 tags until the final flush 42793 if (output.frames) { 42794 return this.pendingMetadata.push(output); 42795 } 42796 42797 // Add this track to the list of pending tracks and store 42798 // important information required for the construction of 42799 // the final segment 42800 this.pendingTracks.push(output.track); 42801 this.pendingBoxes.push(output.boxes); 42802 this.pendingBytes += output.boxes.byteLength; 42803 42804 if (output.track.type === 'video') { 42805 this.videoTrack = output.track; 42806 } 42807 if (output.track.type === 'audio') { 42808 this.audioTrack = output.track; 42809 } 42810 }; 42811}; 42812 42813CoalesceStream.prototype = new Stream(); 42814CoalesceStream.prototype.flush = function(flushSource) { 42815 var 42816 offset = 0, 42817 event = { 42818 captions: [], 42819 captionStreams: {}, 42820 metadata: [], 42821 info: {} 42822 }, 42823 caption, 42824 id3, 42825 initSegment, 42826 timelineStartPts = 0, 42827 i; 42828 42829 if (this.pendingTracks.length < this.numberOfTracks) { 42830 if (flushSource !== 'VideoSegmentStream' && 42831 flushSource !== 'AudioSegmentStream') { 42832 // Return because we haven't received a flush from a data-generating 42833 // portion of the segment (meaning that we have only recieved meta-data 42834 // or captions.) 42835 return; 42836 } else if (this.remuxTracks) { 42837 // Return until we have enough tracks from the pipeline to remux (if we 42838 // are remuxing audio and video into a single MP4) 42839 return; 42840 } else if (this.pendingTracks.length === 0) { 42841 // In the case where we receive a flush without any data having been 42842 // received we consider it an emitted track for the purposes of coalescing 42843 // `done` events. 42844 // We do this for the case where there is an audio and video track in the 42845 // segment but no audio data. (seen in several playlists with alternate 42846 // audio tracks and no audio present in the main TS segments.) 42847 this.emittedTracks++; 42848 42849 if (this.emittedTracks >= this.numberOfTracks) { 42850 this.trigger('done'); 42851 this.emittedTracks = 0; 42852 } 42853 return; 42854 } 42855 } 42856 42857 if (this.videoTrack) { 42858 timelineStartPts = this.videoTrack.timelineStartInfo.pts; 42859 VIDEO_PROPERTIES.forEach(function(prop) { 42860 event.info[prop] = this.videoTrack[prop]; 42861 }, this); 42862 } else if (this.audioTrack) { 42863 timelineStartPts = this.audioTrack.timelineStartInfo.pts; 42864 AUDIO_PROPERTIES.forEach(function(prop) { 42865 event.info[prop] = this.audioTrack[prop]; 42866 }, this); 42867 } 42868 42869 if (this.pendingTracks.length === 1) { 42870 event.type = this.pendingTracks[0].type; 42871 } else { 42872 event.type = 'combined'; 42873 } 42874 42875 this.emittedTracks += this.pendingTracks.length; 42876 42877 initSegment = mp4.initSegment(this.pendingTracks); 42878 42879 // Create a new typed array to hold the init segment 42880 event.initSegment = new Uint8Array(initSegment.byteLength); 42881 42882 // Create an init segment containing a moov 42883 // and track definitions 42884 event.initSegment.set(initSegment); 42885 42886 // Create a new typed array to hold the moof+mdats 42887 event.data = new Uint8Array(this.pendingBytes); 42888 42889 // Append each moof+mdat (one per track) together 42890 for (i = 0; i < this.pendingBoxes.length; i++) { 42891 event.data.set(this.pendingBoxes[i], offset); 42892 offset += this.pendingBoxes[i].byteLength; 42893 } 42894 42895 // Translate caption PTS times into second offsets into the 42896 // video timeline for the segment, and add track info 42897 for (i = 0; i < this.pendingCaptions.length; i++) { 42898 caption = this.pendingCaptions[i]; 42899 caption.startTime = (caption.startPts - timelineStartPts); 42900 caption.startTime /= 90e3; 42901 caption.endTime = (caption.endPts - timelineStartPts); 42902 caption.endTime /= 90e3; 42903 event.captionStreams[caption.stream] = true; 42904 event.captions.push(caption); 42905 } 42906 42907 // Translate ID3 frame PTS times into second offsets into the 42908 // video timeline for the segment 42909 for (i = 0; i < this.pendingMetadata.length; i++) { 42910 id3 = this.pendingMetadata[i]; 42911 id3.cueTime = (id3.pts - timelineStartPts); 42912 id3.cueTime /= 90e3; 42913 event.metadata.push(id3); 42914 } 42915 // We add this to every single emitted segment even though we only need 42916 // it for the first 42917 event.metadata.dispatchType = this.metadataStream.dispatchType; 42918 42919 // Reset stream state 42920 this.pendingTracks.length = 0; 42921 this.videoTrack = null; 42922 this.pendingBoxes.length = 0; 42923 this.pendingCaptions.length = 0; 42924 this.pendingBytes = 0; 42925 this.pendingMetadata.length = 0; 42926 42927 // Emit the built segment 42928 this.trigger('data', event); 42929 42930 // Only emit `done` if all tracks have been flushed and emitted 42931 if (this.emittedTracks >= this.numberOfTracks) { 42932 this.trigger('done'); 42933 this.emittedTracks = 0; 42934 } 42935}; 42936/** 42937 * A Stream that expects MP2T binary data as input and produces 42938 * corresponding media segments, suitable for use with Media Source 42939 * Extension (MSE) implementations that support the ISO BMFF byte 42940 * stream format, like Chrome. 42941 */ 42942Transmuxer = function(options) { 42943 var 42944 self = this, 42945 hasFlushed = true, 42946 videoTrack, 42947 audioTrack; 42948 42949 Transmuxer.prototype.init.call(this); 42950 42951 options = options || {}; 42952 this.baseMediaDecodeTime = options.baseMediaDecodeTime || 0; 42953 this.transmuxPipeline_ = {}; 42954 42955 this.setupAacPipeline = function() { 42956 var pipeline = {}; 42957 this.transmuxPipeline_ = pipeline; 42958 42959 pipeline.type = 'aac'; 42960 pipeline.metadataStream = new m2ts.MetadataStream(); 42961 42962 // set up the parsing pipeline 42963 pipeline.aacStream = new AacStream(); 42964 pipeline.audioTimestampRolloverStream = new m2ts.TimestampRolloverStream('audio'); 42965 pipeline.timedMetadataTimestampRolloverStream = new m2ts.TimestampRolloverStream('timed-metadata'); 42966 pipeline.adtsStream = new AdtsStream(); 42967 pipeline.coalesceStream = new CoalesceStream(options, pipeline.metadataStream); 42968 pipeline.headOfPipeline = pipeline.aacStream; 42969 42970 pipeline.aacStream 42971 .pipe(pipeline.audioTimestampRolloverStream) 42972 .pipe(pipeline.adtsStream); 42973 pipeline.aacStream 42974 .pipe(pipeline.timedMetadataTimestampRolloverStream) 42975 .pipe(pipeline.metadataStream) 42976 .pipe(pipeline.coalesceStream); 42977 42978 pipeline.metadataStream.on('timestamp', function(frame) { 42979 pipeline.aacStream.setTimestamp(frame.timeStamp); 42980 }); 42981 42982 pipeline.aacStream.on('data', function(data) { 42983 if (data.type === 'timed-metadata' && !pipeline.audioSegmentStream) { 42984 audioTrack = audioTrack || { 42985 timelineStartInfo: { 42986 baseMediaDecodeTime: self.baseMediaDecodeTime 42987 }, 42988 codec: 'adts', 42989 type: 'audio' 42990 }; 42991 // hook up the audio segment stream to the first track with aac data 42992 pipeline.coalesceStream.numberOfTracks++; 42993 pipeline.audioSegmentStream = new AudioSegmentStream(audioTrack); 42994 // Set up the final part of the audio pipeline 42995 pipeline.adtsStream 42996 .pipe(pipeline.audioSegmentStream) 42997 .pipe(pipeline.coalesceStream); 42998 } 42999 }); 43000 43001 // Re-emit any data coming from the coalesce stream to the outside world 43002 pipeline.coalesceStream.on('data', this.trigger.bind(this, 'data')); 43003 // Let the consumer know we have finished flushing the entire pipeline 43004 pipeline.coalesceStream.on('done', this.trigger.bind(this, 'done')); 43005 }; 43006 43007 this.setupTsPipeline = function() { 43008 var pipeline = {}; 43009 this.transmuxPipeline_ = pipeline; 43010 43011 pipeline.type = 'ts'; 43012 pipeline.metadataStream = new m2ts.MetadataStream(); 43013 43014 // set up the parsing pipeline 43015 pipeline.packetStream = new m2ts.TransportPacketStream(); 43016 pipeline.parseStream = new m2ts.TransportParseStream(); 43017 pipeline.elementaryStream = new m2ts.ElementaryStream(); 43018 pipeline.videoTimestampRolloverStream = new m2ts.TimestampRolloverStream('video'); 43019 pipeline.audioTimestampRolloverStream = new m2ts.TimestampRolloverStream('audio'); 43020 pipeline.timedMetadataTimestampRolloverStream = new m2ts.TimestampRolloverStream('timed-metadata'); 43021 pipeline.adtsStream = new AdtsStream(); 43022 pipeline.h264Stream = new H264Stream(); 43023 pipeline.captionStream = new m2ts.CaptionStream(); 43024 pipeline.coalesceStream = new CoalesceStream(options, pipeline.metadataStream); 43025 pipeline.headOfPipeline = pipeline.packetStream; 43026 43027 // disassemble MPEG2-TS packets into elementary streams 43028 pipeline.packetStream 43029 .pipe(pipeline.parseStream) 43030 .pipe(pipeline.elementaryStream); 43031 43032 // !!THIS ORDER IS IMPORTANT!! 43033 // demux the streams 43034 pipeline.elementaryStream 43035 .pipe(pipeline.videoTimestampRolloverStream) 43036 .pipe(pipeline.h264Stream); 43037 pipeline.elementaryStream 43038 .pipe(pipeline.audioTimestampRolloverStream) 43039 .pipe(pipeline.adtsStream); 43040 43041 pipeline.elementaryStream 43042 .pipe(pipeline.timedMetadataTimestampRolloverStream) 43043 .pipe(pipeline.metadataStream) 43044 .pipe(pipeline.coalesceStream); 43045 43046 // Hook up CEA-608/708 caption stream 43047 pipeline.h264Stream.pipe(pipeline.captionStream) 43048 .pipe(pipeline.coalesceStream); 43049 43050 pipeline.elementaryStream.on('data', function(data) { 43051 var i; 43052 43053 if (data.type === 'metadata') { 43054 i = data.tracks.length; 43055 43056 // scan the tracks listed in the metadata 43057 while (i--) { 43058 if (!videoTrack && data.tracks[i].type === 'video') { 43059 videoTrack = data.tracks[i]; 43060 videoTrack.timelineStartInfo.baseMediaDecodeTime = self.baseMediaDecodeTime; 43061 } else if (!audioTrack && data.tracks[i].type === 'audio') { 43062 audioTrack = data.tracks[i]; 43063 audioTrack.timelineStartInfo.baseMediaDecodeTime = self.baseMediaDecodeTime; 43064 } 43065 } 43066 43067 // hook up the video segment stream to the first track with h264 data 43068 if (videoTrack && !pipeline.videoSegmentStream) { 43069 pipeline.coalesceStream.numberOfTracks++; 43070 pipeline.videoSegmentStream = new VideoSegmentStream(videoTrack); 43071 43072 pipeline.videoSegmentStream.on('timelineStartInfo', function(timelineStartInfo) { 43073 // When video emits timelineStartInfo data after a flush, we forward that 43074 // info to the AudioSegmentStream, if it exists, because video timeline 43075 // data takes precedence. 43076 if (audioTrack) { 43077 audioTrack.timelineStartInfo = timelineStartInfo; 43078 // On the first segment we trim AAC frames that exist before the 43079 // very earliest DTS we have seen in video because Chrome will 43080 // interpret any video track with a baseMediaDecodeTime that is 43081 // non-zero as a gap. 43082 pipeline.audioSegmentStream.setEarliestDts(timelineStartInfo.dts); 43083 } 43084 }); 43085 43086 pipeline.videoSegmentStream.on('baseMediaDecodeTime', function(baseMediaDecodeTime) { 43087 if (audioTrack) { 43088 pipeline.audioSegmentStream.setVideoBaseMediaDecodeTime(baseMediaDecodeTime); 43089 } 43090 }); 43091 43092 // Set up the final part of the video pipeline 43093 pipeline.h264Stream 43094 .pipe(pipeline.videoSegmentStream) 43095 .pipe(pipeline.coalesceStream); 43096 } 43097 43098 if (audioTrack && !pipeline.audioSegmentStream) { 43099 // hook up the audio segment stream to the first track with aac data 43100 pipeline.coalesceStream.numberOfTracks++; 43101 pipeline.audioSegmentStream = new AudioSegmentStream(audioTrack); 43102 43103 // Set up the final part of the audio pipeline 43104 pipeline.adtsStream 43105 .pipe(pipeline.audioSegmentStream) 43106 .pipe(pipeline.coalesceStream); 43107 } 43108 } 43109 }); 43110 43111 // Re-emit any data coming from the coalesce stream to the outside world 43112 pipeline.coalesceStream.on('data', this.trigger.bind(this, 'data')); 43113 // Let the consumer know we have finished flushing the entire pipeline 43114 pipeline.coalesceStream.on('done', this.trigger.bind(this, 'done')); 43115 }; 43116 43117 // hook up the segment streams once track metadata is delivered 43118 this.setBaseMediaDecodeTime = function(baseMediaDecodeTime) { 43119 var pipeline = this.transmuxPipeline_; 43120 43121 this.baseMediaDecodeTime = baseMediaDecodeTime; 43122 if (audioTrack) { 43123 audioTrack.timelineStartInfo.dts = undefined; 43124 audioTrack.timelineStartInfo.pts = undefined; 43125 clearDtsInfo(audioTrack); 43126 audioTrack.timelineStartInfo.baseMediaDecodeTime = baseMediaDecodeTime; 43127 if (pipeline.audioTimestampRolloverStream) { 43128 pipeline.audioTimestampRolloverStream.discontinuity(); 43129 } 43130 } 43131 if (videoTrack) { 43132 if (pipeline.videoSegmentStream) { 43133 pipeline.videoSegmentStream.gopCache_ = []; 43134 pipeline.videoTimestampRolloverStream.discontinuity(); 43135 } 43136 videoTrack.timelineStartInfo.dts = undefined; 43137 videoTrack.timelineStartInfo.pts = undefined; 43138 clearDtsInfo(videoTrack); 43139 pipeline.captionStream.reset(); 43140 videoTrack.timelineStartInfo.baseMediaDecodeTime = baseMediaDecodeTime; 43141 } 43142 43143 if (pipeline.timedMetadataTimestampRolloverStream) { 43144 pipeline.timedMetadataTimestampRolloverStream.discontinuity(); 43145 } 43146 }; 43147 43148 this.setAudioAppendStart = function(timestamp) { 43149 if (audioTrack) { 43150 this.transmuxPipeline_.audioSegmentStream.setAudioAppendStart(timestamp); 43151 } 43152 }; 43153 43154 // feed incoming data to the front of the parsing pipeline 43155 this.push = function(data) { 43156 if (hasFlushed) { 43157 var isAac = isLikelyAacData(data); 43158 43159 if (isAac && this.transmuxPipeline_.type !== 'aac') { 43160 this.setupAacPipeline(); 43161 } else if (!isAac && this.transmuxPipeline_.type !== 'ts') { 43162 this.setupTsPipeline(); 43163 } 43164 hasFlushed = false; 43165 } 43166 this.transmuxPipeline_.headOfPipeline.push(data); 43167 }; 43168 43169 // flush any buffered data 43170 this.flush = function() { 43171 hasFlushed = true; 43172 // Start at the top of the pipeline and flush all pending work 43173 this.transmuxPipeline_.headOfPipeline.flush(); 43174 }; 43175 43176 // Caption data has to be reset when seeking outside buffered range 43177 this.resetCaptions = function() { 43178 if (this.transmuxPipeline_.captionStream) { 43179 this.transmuxPipeline_.captionStream.reset(); 43180 } 43181 }; 43182 43183}; 43184Transmuxer.prototype = new Stream(); 43185 43186module.exports = { 43187 Transmuxer: Transmuxer, 43188 VideoSegmentStream: VideoSegmentStream, 43189 AudioSegmentStream: AudioSegmentStream, 43190 AUDIO_PROPERTIES: AUDIO_PROPERTIES, 43191 VIDEO_PROPERTIES: VIDEO_PROPERTIES 43192}; 43193 43194},{"../aac":38,"../codecs/adts.js":40,"../codecs/h264":41,"../data/silence":42,"../m2ts/m2ts.js":50,"../utils/clock":60,"../utils/stream.js":62,"./mp4-generator.js":56}],59:[function(require,module,exports){ 43195/** 43196 * mux.js 43197 * 43198 * Copyright (c) 2016 Brightcove 43199 * All rights reserved. 43200 * 43201 * Parse mpeg2 transport stream packets to extract basic timing information 43202 */ 43203'use strict'; 43204 43205var StreamTypes = require('../m2ts/stream-types.js'); 43206var handleRollover = require('../m2ts/timestamp-rollover-stream.js').handleRollover; 43207var probe = {}; 43208probe.ts = require('../m2ts/probe.js'); 43209probe.aac = require('../aac/probe.js'); 43210 43211 43212var 43213 PES_TIMESCALE = 90000, 43214 MP2T_PACKET_LENGTH = 188, // bytes 43215 SYNC_BYTE = 0x47; 43216 43217var isLikelyAacData = function(data) { 43218 if ((data[0] === 'I'.charCodeAt(0)) && 43219 (data[1] === 'D'.charCodeAt(0)) && 43220 (data[2] === '3'.charCodeAt(0))) { 43221 return true; 43222 } 43223 return false; 43224}; 43225 43226/** 43227 * walks through segment data looking for pat and pmt packets to parse out 43228 * program map table information 43229 */ 43230var parsePsi_ = function(bytes, pmt) { 43231 var 43232 startIndex = 0, 43233 endIndex = MP2T_PACKET_LENGTH, 43234 packet, type; 43235 43236 while (endIndex < bytes.byteLength) { 43237 // Look for a pair of start and end sync bytes in the data.. 43238 if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) { 43239 // We found a packet 43240 packet = bytes.subarray(startIndex, endIndex); 43241 type = probe.ts.parseType(packet, pmt.pid); 43242 43243 switch (type) { 43244 case 'pat': 43245 if (!pmt.pid) { 43246 pmt.pid = probe.ts.parsePat(packet); 43247 } 43248 break; 43249 case 'pmt': 43250 if (!pmt.table) { 43251 pmt.table = probe.ts.parsePmt(packet); 43252 } 43253 break; 43254 default: 43255 break; 43256 } 43257 43258 // Found the pat and pmt, we can stop walking the segment 43259 if (pmt.pid && pmt.table) { 43260 return; 43261 } 43262 43263 startIndex += MP2T_PACKET_LENGTH; 43264 endIndex += MP2T_PACKET_LENGTH; 43265 continue; 43266 } 43267 43268 // If we get here, we have somehow become de-synchronized and we need to step 43269 // forward one byte at a time until we find a pair of sync bytes that denote 43270 // a packet 43271 startIndex++; 43272 endIndex++; 43273 } 43274}; 43275 43276/** 43277 * walks through the segment data from the start and end to get timing information 43278 * for the first and last audio pes packets 43279 */ 43280var parseAudioPes_ = function(bytes, pmt, result) { 43281 var 43282 startIndex = 0, 43283 endIndex = MP2T_PACKET_LENGTH, 43284 packet, type, pesType, pusi, parsed; 43285 43286 var endLoop = false; 43287 43288 // Start walking from start of segment to get first audio packet 43289 while (endIndex < bytes.byteLength) { 43290 // Look for a pair of start and end sync bytes in the data.. 43291 if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) { 43292 // We found a packet 43293 packet = bytes.subarray(startIndex, endIndex); 43294 type = probe.ts.parseType(packet, pmt.pid); 43295 43296 switch (type) { 43297 case 'pes': 43298 pesType = probe.ts.parsePesType(packet, pmt.table); 43299 pusi = probe.ts.parsePayloadUnitStartIndicator(packet); 43300 if (pesType === 'audio' && pusi) { 43301 parsed = probe.ts.parsePesTime(packet); 43302 if (parsed) { 43303 parsed.type = 'audio'; 43304 result.audio.push(parsed); 43305 endLoop = true; 43306 } 43307 } 43308 break; 43309 default: 43310 break; 43311 } 43312 43313 if (endLoop) { 43314 break; 43315 } 43316 43317 startIndex += MP2T_PACKET_LENGTH; 43318 endIndex += MP2T_PACKET_LENGTH; 43319 continue; 43320 } 43321 43322 // If we get here, we have somehow become de-synchronized and we need to step 43323 // forward one byte at a time until we find a pair of sync bytes that denote 43324 // a packet 43325 startIndex++; 43326 endIndex++; 43327 } 43328 43329 // Start walking from end of segment to get last audio packet 43330 endIndex = bytes.byteLength; 43331 startIndex = endIndex - MP2T_PACKET_LENGTH; 43332 endLoop = false; 43333 while (startIndex >= 0) { 43334 // Look for a pair of start and end sync bytes in the data.. 43335 if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) { 43336 // We found a packet 43337 packet = bytes.subarray(startIndex, endIndex); 43338 type = probe.ts.parseType(packet, pmt.pid); 43339 43340 switch (type) { 43341 case 'pes': 43342 pesType = probe.ts.parsePesType(packet, pmt.table); 43343 pusi = probe.ts.parsePayloadUnitStartIndicator(packet); 43344 if (pesType === 'audio' && pusi) { 43345 parsed = probe.ts.parsePesTime(packet); 43346 if (parsed) { 43347 parsed.type = 'audio'; 43348 result.audio.push(parsed); 43349 endLoop = true; 43350 } 43351 } 43352 break; 43353 default: 43354 break; 43355 } 43356 43357 if (endLoop) { 43358 break; 43359 } 43360 43361 startIndex -= MP2T_PACKET_LENGTH; 43362 endIndex -= MP2T_PACKET_LENGTH; 43363 continue; 43364 } 43365 43366 // If we get here, we have somehow become de-synchronized and we need to step 43367 // forward one byte at a time until we find a pair of sync bytes that denote 43368 // a packet 43369 startIndex--; 43370 endIndex--; 43371 } 43372}; 43373 43374/** 43375 * walks through the segment data from the start and end to get timing information 43376 * for the first and last video pes packets as well as timing information for the first 43377 * key frame. 43378 */ 43379var parseVideoPes_ = function(bytes, pmt, result) { 43380 var 43381 startIndex = 0, 43382 endIndex = MP2T_PACKET_LENGTH, 43383 packet, type, pesType, pusi, parsed, frame, i, pes; 43384 43385 var endLoop = false; 43386 43387 var currentFrame = { 43388 data: [], 43389 size: 0 43390 }; 43391 43392 // Start walking from start of segment to get first video packet 43393 while (endIndex < bytes.byteLength) { 43394 // Look for a pair of start and end sync bytes in the data.. 43395 if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) { 43396 // We found a packet 43397 packet = bytes.subarray(startIndex, endIndex); 43398 type = probe.ts.parseType(packet, pmt.pid); 43399 43400 switch (type) { 43401 case 'pes': 43402 pesType = probe.ts.parsePesType(packet, pmt.table); 43403 pusi = probe.ts.parsePayloadUnitStartIndicator(packet); 43404 if (pesType === 'video') { 43405 if (pusi && !endLoop) { 43406 parsed = probe.ts.parsePesTime(packet); 43407 if (parsed) { 43408 parsed.type = 'video'; 43409 result.video.push(parsed); 43410 endLoop = true; 43411 } 43412 } 43413 if (!result.firstKeyFrame) { 43414 if (pusi) { 43415 if (currentFrame.size !== 0) { 43416 frame = new Uint8Array(currentFrame.size); 43417 i = 0; 43418 while (currentFrame.data.length) { 43419 pes = currentFrame.data.shift(); 43420 frame.set(pes, i); 43421 i += pes.byteLength; 43422 } 43423 if (probe.ts.videoPacketContainsKeyFrame(frame)) { 43424 result.firstKeyFrame = probe.ts.parsePesTime(frame); 43425 result.firstKeyFrame.type = 'video'; 43426 } 43427 currentFrame.size = 0; 43428 } 43429 } 43430 currentFrame.data.push(packet); 43431 currentFrame.size += packet.byteLength; 43432 } 43433 } 43434 break; 43435 default: 43436 break; 43437 } 43438 43439 if (endLoop && result.firstKeyFrame) { 43440 break; 43441 } 43442 43443 startIndex += MP2T_PACKET_LENGTH; 43444 endIndex += MP2T_PACKET_LENGTH; 43445 continue; 43446 } 43447 43448 // If we get here, we have somehow become de-synchronized and we need to step 43449 // forward one byte at a time until we find a pair of sync bytes that denote 43450 // a packet 43451 startIndex++; 43452 endIndex++; 43453 } 43454 43455 // Start walking from end of segment to get last video packet 43456 endIndex = bytes.byteLength; 43457 startIndex = endIndex - MP2T_PACKET_LENGTH; 43458 endLoop = false; 43459 while (startIndex >= 0) { 43460 // Look for a pair of start and end sync bytes in the data.. 43461 if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) { 43462 // We found a packet 43463 packet = bytes.subarray(startIndex, endIndex); 43464 type = probe.ts.parseType(packet, pmt.pid); 43465 43466 switch (type) { 43467 case 'pes': 43468 pesType = probe.ts.parsePesType(packet, pmt.table); 43469 pusi = probe.ts.parsePayloadUnitStartIndicator(packet); 43470 if (pesType === 'video' && pusi) { 43471 parsed = probe.ts.parsePesTime(packet); 43472 if (parsed) { 43473 parsed.type = 'video'; 43474 result.video.push(parsed); 43475 endLoop = true; 43476 } 43477 } 43478 break; 43479 default: 43480 break; 43481 } 43482 43483 if (endLoop) { 43484 break; 43485 } 43486 43487 startIndex -= MP2T_PACKET_LENGTH; 43488 endIndex -= MP2T_PACKET_LENGTH; 43489 continue; 43490 } 43491 43492 // If we get here, we have somehow become de-synchronized and we need to step 43493 // forward one byte at a time until we find a pair of sync bytes that denote 43494 // a packet 43495 startIndex--; 43496 endIndex--; 43497 } 43498}; 43499 43500/** 43501 * Adjusts the timestamp information for the segment to account for 43502 * rollover and convert to seconds based on pes packet timescale (90khz clock) 43503 */ 43504var adjustTimestamp_ = function(segmentInfo, baseTimestamp) { 43505 if (segmentInfo.audio && segmentInfo.audio.length) { 43506 var audioBaseTimestamp = baseTimestamp; 43507 if (typeof audioBaseTimestamp === 'undefined') { 43508 audioBaseTimestamp = segmentInfo.audio[0].dts; 43509 } 43510 segmentInfo.audio.forEach(function(info) { 43511 info.dts = handleRollover(info.dts, audioBaseTimestamp); 43512 info.pts = handleRollover(info.pts, audioBaseTimestamp); 43513 // time in seconds 43514 info.dtsTime = info.dts / PES_TIMESCALE; 43515 info.ptsTime = info.pts / PES_TIMESCALE; 43516 }); 43517 } 43518 43519 if (segmentInfo.video && segmentInfo.video.length) { 43520 var videoBaseTimestamp = baseTimestamp; 43521 if (typeof videoBaseTimestamp === 'undefined') { 43522 videoBaseTimestamp = segmentInfo.video[0].dts; 43523 } 43524 segmentInfo.video.forEach(function(info) { 43525 info.dts = handleRollover(info.dts, videoBaseTimestamp); 43526 info.pts = handleRollover(info.pts, videoBaseTimestamp); 43527 // time in seconds 43528 info.dtsTime = info.dts / PES_TIMESCALE; 43529 info.ptsTime = info.pts / PES_TIMESCALE; 43530 }); 43531 if (segmentInfo.firstKeyFrame) { 43532 var frame = segmentInfo.firstKeyFrame; 43533 frame.dts = handleRollover(frame.dts, videoBaseTimestamp); 43534 frame.pts = handleRollover(frame.pts, videoBaseTimestamp); 43535 // time in seconds 43536 frame.dtsTime = frame.dts / PES_TIMESCALE; 43537 frame.ptsTime = frame.dts / PES_TIMESCALE; 43538 } 43539 } 43540}; 43541 43542/** 43543 * inspects the aac data stream for start and end time information 43544 */ 43545var inspectAac_ = function(bytes) { 43546 var 43547 endLoop = false, 43548 audioCount = 0, 43549 sampleRate = null, 43550 timestamp = null, 43551 frameSize = 0, 43552 byteIndex = 0, 43553 packet; 43554 43555 while (bytes.length - byteIndex >= 3) { 43556 var type = probe.aac.parseType(bytes, byteIndex); 43557 switch (type) { 43558 case 'timed-metadata': 43559 // Exit early because we don't have enough to parse 43560 // the ID3 tag header 43561 if (bytes.length - byteIndex < 10) { 43562 endLoop = true; 43563 break; 43564 } 43565 43566 frameSize = probe.aac.parseId3TagSize(bytes, byteIndex); 43567 43568 // Exit early if we don't have enough in the buffer 43569 // to emit a full packet 43570 if (frameSize > bytes.length) { 43571 endLoop = true; 43572 break; 43573 } 43574 if (timestamp === null) { 43575 packet = bytes.subarray(byteIndex, byteIndex + frameSize); 43576 timestamp = probe.aac.parseAacTimestamp(packet); 43577 } 43578 byteIndex += frameSize; 43579 break; 43580 case 'audio': 43581 // Exit early because we don't have enough to parse 43582 // the ADTS frame header 43583 if (bytes.length - byteIndex < 7) { 43584 endLoop = true; 43585 break; 43586 } 43587 43588 frameSize = probe.aac.parseAdtsSize(bytes, byteIndex); 43589 43590 // Exit early if we don't have enough in the buffer 43591 // to emit a full packet 43592 if (frameSize > bytes.length) { 43593 endLoop = true; 43594 break; 43595 } 43596 if (sampleRate === null) { 43597 packet = bytes.subarray(byteIndex, byteIndex + frameSize); 43598 sampleRate = probe.aac.parseSampleRate(packet); 43599 } 43600 audioCount++; 43601 byteIndex += frameSize; 43602 break; 43603 default: 43604 byteIndex++; 43605 break; 43606 } 43607 if (endLoop) { 43608 return null; 43609 } 43610 } 43611 if (sampleRate === null || timestamp === null) { 43612 return null; 43613 } 43614 43615 var audioTimescale = PES_TIMESCALE / sampleRate; 43616 43617 var result = { 43618 audio: [ 43619 { 43620 type: 'audio', 43621 dts: timestamp, 43622 pts: timestamp 43623 }, 43624 { 43625 type: 'audio', 43626 dts: timestamp + (audioCount * 1024 * audioTimescale), 43627 pts: timestamp + (audioCount * 1024 * audioTimescale) 43628 } 43629 ] 43630 }; 43631 43632 return result; 43633}; 43634 43635/** 43636 * inspects the transport stream segment data for start and end time information 43637 * of the audio and video tracks (when present) as well as the first key frame's 43638 * start time. 43639 */ 43640var inspectTs_ = function(bytes) { 43641 var pmt = { 43642 pid: null, 43643 table: null 43644 }; 43645 43646 var result = {}; 43647 43648 parsePsi_(bytes, pmt); 43649 43650 for (var pid in pmt.table) { 43651 if (pmt.table.hasOwnProperty(pid)) { 43652 var type = pmt.table[pid]; 43653 switch (type) { 43654 case StreamTypes.H264_STREAM_TYPE: 43655 result.video = []; 43656 parseVideoPes_(bytes, pmt, result); 43657 if (result.video.length === 0) { 43658 delete result.video; 43659 } 43660 break; 43661 case StreamTypes.ADTS_STREAM_TYPE: 43662 result.audio = []; 43663 parseAudioPes_(bytes, pmt, result); 43664 if (result.audio.length === 0) { 43665 delete result.audio; 43666 } 43667 break; 43668 default: 43669 break; 43670 } 43671 } 43672 } 43673 return result; 43674}; 43675 43676/** 43677 * Inspects segment byte data and returns an object with start and end timing information 43678 * 43679 * @param {Uint8Array} bytes The segment byte data 43680 * @param {Number} baseTimestamp Relative reference timestamp used when adjusting frame 43681 * timestamps for rollover. This value must be in 90khz clock. 43682 * @return {Object} Object containing start and end frame timing info of segment. 43683 */ 43684var inspect = function(bytes, baseTimestamp) { 43685 var isAacData = isLikelyAacData(bytes); 43686 43687 var result; 43688 43689 if (isAacData) { 43690 result = inspectAac_(bytes); 43691 } else { 43692 result = inspectTs_(bytes); 43693 } 43694 43695 if (!result || (!result.audio && !result.video)) { 43696 return null; 43697 } 43698 43699 adjustTimestamp_(result, baseTimestamp); 43700 43701 return result; 43702}; 43703 43704module.exports = { 43705 inspect: inspect 43706}; 43707 43708},{"../aac/probe.js":39,"../m2ts/probe.js":52,"../m2ts/stream-types.js":53,"../m2ts/timestamp-rollover-stream.js":54}],60:[function(require,module,exports){ 43709var 43710 ONE_SECOND_IN_TS = 90000, // 90kHz clock 43711 secondsToVideoTs, 43712 secondsToAudioTs, 43713 videoTsToSeconds, 43714 audioTsToSeconds, 43715 audioTsToVideoTs, 43716 videoTsToAudioTs; 43717 43718secondsToVideoTs = function(seconds) { 43719 return seconds * ONE_SECOND_IN_TS; 43720}; 43721 43722secondsToAudioTs = function(seconds, sampleRate) { 43723 return seconds * sampleRate; 43724}; 43725 43726videoTsToSeconds = function(timestamp) { 43727 return timestamp / ONE_SECOND_IN_TS; 43728}; 43729 43730audioTsToSeconds = function(timestamp, sampleRate) { 43731 return timestamp / sampleRate; 43732}; 43733 43734audioTsToVideoTs = function(timestamp, sampleRate) { 43735 return secondsToVideoTs(audioTsToSeconds(timestamp, sampleRate)); 43736}; 43737 43738videoTsToAudioTs = function(timestamp, sampleRate) { 43739 return secondsToAudioTs(videoTsToSeconds(timestamp), sampleRate); 43740}; 43741 43742module.exports = { 43743 secondsToVideoTs: secondsToVideoTs, 43744 secondsToAudioTs: secondsToAudioTs, 43745 videoTsToSeconds: videoTsToSeconds, 43746 audioTsToSeconds: audioTsToSeconds, 43747 audioTsToVideoTs: audioTsToVideoTs, 43748 videoTsToAudioTs: videoTsToAudioTs 43749}; 43750 43751},{}],61:[function(require,module,exports){ 43752'use strict'; 43753 43754var ExpGolomb; 43755 43756/** 43757 * Parser for exponential Golomb codes, a variable-bitwidth number encoding 43758 * scheme used by h264. 43759 */ 43760ExpGolomb = function(workingData) { 43761 var 43762 // the number of bytes left to examine in workingData 43763 workingBytesAvailable = workingData.byteLength, 43764 43765 // the current word being examined 43766 workingWord = 0, // :uint 43767 43768 // the number of bits left to examine in the current word 43769 workingBitsAvailable = 0; // :uint; 43770 43771 // ():uint 43772 this.length = function() { 43773 return (8 * workingBytesAvailable); 43774 }; 43775 43776 // ():uint 43777 this.bitsAvailable = function() { 43778 return (8 * workingBytesAvailable) + workingBitsAvailable; 43779 }; 43780 43781 // ():void 43782 this.loadWord = function() { 43783 var 43784 position = workingData.byteLength - workingBytesAvailable, 43785 workingBytes = new Uint8Array(4), 43786 availableBytes = Math.min(4, workingBytesAvailable); 43787 43788 if (availableBytes === 0) { 43789 throw new Error('no bytes available'); 43790 } 43791 43792 workingBytes.set(workingData.subarray(position, 43793 position + availableBytes)); 43794 workingWord = new DataView(workingBytes.buffer).getUint32(0); 43795 43796 // track the amount of workingData that has been processed 43797 workingBitsAvailable = availableBytes * 8; 43798 workingBytesAvailable -= availableBytes; 43799 }; 43800 43801 // (count:int):void 43802 this.skipBits = function(count) { 43803 var skipBytes; // :int 43804 if (workingBitsAvailable > count) { 43805 workingWord <<= count; 43806 workingBitsAvailable -= count; 43807 } else { 43808 count -= workingBitsAvailable; 43809 skipBytes = Math.floor(count / 8); 43810 43811 count -= (skipBytes * 8); 43812 workingBytesAvailable -= skipBytes; 43813 43814 this.loadWord(); 43815 43816 workingWord <<= count; 43817 workingBitsAvailable -= count; 43818 } 43819 }; 43820 43821 // (size:int):uint 43822 this.readBits = function(size) { 43823 var 43824 bits = Math.min(workingBitsAvailable, size), // :uint 43825 valu = workingWord >>> (32 - bits); // :uint 43826 // if size > 31, handle error 43827 workingBitsAvailable -= bits; 43828 if (workingBitsAvailable > 0) { 43829 workingWord <<= bits; 43830 } else if (workingBytesAvailable > 0) { 43831 this.loadWord(); 43832 } 43833 43834 bits = size - bits; 43835 if (bits > 0) { 43836 return valu << bits | this.readBits(bits); 43837 } 43838 return valu; 43839 }; 43840 43841 // ():uint 43842 this.skipLeadingZeros = function() { 43843 var leadingZeroCount; // :uint 43844 for (leadingZeroCount = 0; leadingZeroCount < workingBitsAvailable; ++leadingZeroCount) { 43845 if ((workingWord & (0x80000000 >>> leadingZeroCount)) !== 0) { 43846 // the first bit of working word is 1 43847 workingWord <<= leadingZeroCount; 43848 workingBitsAvailable -= leadingZeroCount; 43849 return leadingZeroCount; 43850 } 43851 } 43852 43853 // we exhausted workingWord and still have not found a 1 43854 this.loadWord(); 43855 return leadingZeroCount + this.skipLeadingZeros(); 43856 }; 43857 43858 // ():void 43859 this.skipUnsignedExpGolomb = function() { 43860 this.skipBits(1 + this.skipLeadingZeros()); 43861 }; 43862 43863 // ():void 43864 this.skipExpGolomb = function() { 43865 this.skipBits(1 + this.skipLeadingZeros()); 43866 }; 43867 43868 // ():uint 43869 this.readUnsignedExpGolomb = function() { 43870 var clz = this.skipLeadingZeros(); // :uint 43871 return this.readBits(clz + 1) - 1; 43872 }; 43873 43874 // ():int 43875 this.readExpGolomb = function() { 43876 var valu = this.readUnsignedExpGolomb(); // :int 43877 if (0x01 & valu) { 43878 // the number is odd if the low order bit is set 43879 return (1 + valu) >>> 1; // add 1 to make it even, and divide by 2 43880 } 43881 return -1 * (valu >>> 1); // divide by two then make it negative 43882 }; 43883 43884 // Some convenience functions 43885 // :Boolean 43886 this.readBoolean = function() { 43887 return this.readBits(1) === 1; 43888 }; 43889 43890 // ():int 43891 this.readUnsignedByte = function() { 43892 return this.readBits(8); 43893 }; 43894 43895 this.loadWord(); 43896}; 43897 43898module.exports = ExpGolomb; 43899 43900},{}],62:[function(require,module,exports){ 43901/** 43902 * mux.js 43903 * 43904 * Copyright (c) 2014 Brightcove 43905 * All rights reserved. 43906 * 43907 * A lightweight readable stream implemention that handles event dispatching. 43908 * Objects that inherit from streams should call init in their constructors. 43909 */ 43910'use strict'; 43911 43912var Stream = function() { 43913 this.init = function() { 43914 var listeners = {}; 43915 /** 43916 * Add a listener for a specified event type. 43917 * @param type {string} the event name 43918 * @param listener {function} the callback to be invoked when an event of 43919 * the specified type occurs 43920 */ 43921 this.on = function(type, listener) { 43922 if (!listeners[type]) { 43923 listeners[type] = []; 43924 } 43925 listeners[type] = listeners[type].concat(listener); 43926 }; 43927 /** 43928 * Remove a listener for a specified event type. 43929 * @param type {string} the event name 43930 * @param listener {function} a function previously registered for this 43931 * type of event through `on` 43932 */ 43933 this.off = function(type, listener) { 43934 var index; 43935 if (!listeners[type]) { 43936 return false;
43937 } 43938 index = listeners[type].indexOf(listener); 43939 listeners[type] = listeners[type].slice(); 43940 listeners[type].splice(index, 1); 43941 return index > -1; 43942 }; 43943 /** 43944 * Trigger an event of the specified type on this stream. Any additional 43945 * arguments to this function are passed as parameters to event listeners. 43946 * @param type {string} the event name 43947 */ 43948 this.trigger = function(type) { 43949 var callbacks, i, length, args; 43950 callbacks = listeners[type]; 43951 if (!callbacks) { 43952 return; 43953 } 43954 // Slicing the arguments on every invocation of this method 43955 // can add a significant amount of overhead. Avoid the 43956 // intermediate object creation for the common case of a 43957 // single callback argument 43958 if (arguments.length === 2) { 43959 length = callbacks.length; 43960 for (i = 0; i < length; ++i) { 43961 callbacks[i].call(this, arguments[1]); 43962 } 43963 } else { 43964 args = []; 43965 i = arguments.length; 43966 for (i = 1; i < arguments.length; ++i) { 43967 args.push(arguments[i]); 43968 } 43969 length = callbacks.length; 43970 for (i = 0; i < length; ++i) { 43971 callbacks[i].apply(this, args); 43972 } 43973 } 43974 }; 43975 /** 43976 * Destroys the stream and cleans up. 43977 */ 43978 this.dispose = function() { 43979 listeners = {}; 43980 }; 43981 }; 43982}; 43983 43984/** 43985 * Forwards all `data` events on this stream to the destination stream. The 43986 * destination stream should provide a method `push` to receive the data 43987 * events as they arrive. 43988 * @param destination {stream} the stream that will receive all `data` events 43989 * @param autoFlush {boolean} if false, we will not call `flush` on the destination 43990 * when the current stream emits a 'done' event 43991 * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options 43992 */ 43993Stream.prototype.pipe = function(destination) { 43994 this.on('data', function(data) { 43995 destination.push(data); 43996 }); 43997 43998 this.on('done', function(flushSource) { 43999 destination.flush(flushSource); 44000 }); 44001 44002 return destination; 44003}; 44004 44005// Default stream functions that are expected to be overridden to perform 44006// actual work. These are provided by the prototype as a sort of no-op 44007// implementation so that we don't have to check for their existence in the 44008// `pipe` function above. 44009Stream.prototype.push = function(data) { 44010 this.trigger('data', data); 44011}; 44012 44013Stream.prototype.flush = function(flushSource) { 44014 this.trigger('done', flushSource); 44015}; 44016 44017module.exports = Stream; 44018 44019},{}],63:[function(require,module,exports){ 44020/* jshint ignore:start */ 44021(function(root) { 44022/* jshint ignore:end */ 44023 var URLToolkit = { 44024 // build an absolute URL from a relative one using the provided baseURL 44025 // if relativeURL is an absolute URL it will be returned as is. 44026 buildAbsoluteURL: function(baseURL, relativeURL) { 44027 // remove any remaining space and CRLF 44028 relativeURL = relativeURL.trim(); 44029 if (/^[a-z]+:/i.test(relativeURL)) { 44030 // complete url, not relative 44031 return relativeURL; 44032 } 44033 44034 var relativeURLQuery = null; 44035 var relativeURLHash = null; 44036 44037 var relativeURLHashSplit = /^([^#]*)(.*)$/.exec(relativeURL); 44038 if (relativeURLHashSplit) { 44039 relativeURLHash = relativeURLHashSplit[2]; 44040 relativeURL = relativeURLHashSplit[1]; 44041 } 44042 var relativeURLQuerySplit = /^([^\?]*)(.*)$/.exec(relativeURL); 44043 if (relativeURLQuerySplit) { 44044 relativeURLQuery = relativeURLQuerySplit[2]; 44045 relativeURL = relativeURLQuerySplit[1]; 44046 } 44047 44048 var baseURLHashSplit = /^([^#]*)(.*)$/.exec(baseURL); 44049 if (baseURLHashSplit) { 44050 baseURL = baseURLHashSplit[1]; 44051 } 44052 var baseURLQuerySplit = /^([^\?]*)(.*)$/.exec(baseURL); 44053 if (baseURLQuerySplit) { 44054 baseURL = baseURLQuerySplit[1]; 44055 } 44056 44057 var baseURLDomainSplit = /^(([a-z]+:)?\/\/[^:\/]+(:[0-9]+)?)?(\/?.*)$/i.exec(baseURL); 44058 if (!baseURLDomainSplit) { 44059 throw new Error('Error trying to parse base URL.'); 44060 } 44061 44062 // e.g. 'http:', 'https:', '' 44063 var baseURLProtocol = baseURLDomainSplit[2] || ''; 44064 // e.g. 'http://example.com', '//example.com', '' 44065 var baseURLProtocolDomain = baseURLDomainSplit[1] || ''; 44066 // e.g. '/a/b/c/playlist.m3u8', 'a/b/c/playlist.m3u8' 44067 var baseURLPath = baseURLDomainSplit[4]; 44068 if (baseURLPath.indexOf('/') !== 0 && baseURLProtocolDomain !== '') { 44069 // this handles a base url of http://example.com (missing last slash) 44070 baseURLPath = '/'+baseURLPath; 44071 } 44072 44073 var builtURL = null; 44074 if (/^\/\//.test(relativeURL)) { 44075 // relative url starts wth '//' so copy protocol (which may be '' if baseUrl didn't provide one) 44076 builtURL = baseURLProtocol+'//'+URLToolkit.buildAbsolutePath('', relativeURL.substring(2)); 44077 } 44078 else if (/^\//.test(relativeURL)) { 44079 // relative url starts with '/' so start from root of domain 44080 builtURL = baseURLProtocolDomain+'/'+URLToolkit.buildAbsolutePath('', relativeURL.substring(1)); 44081 } 44082 else { 44083 builtURL = URLToolkit.buildAbsolutePath(baseURLProtocolDomain+baseURLPath, relativeURL); 44084 } 44085 44086 // put the query and hash parts back 44087 if (relativeURLQuery) { 44088 builtURL += relativeURLQuery; 44089 } 44090 if (relativeURLHash) { 44091 builtURL += relativeURLHash; 44092 } 44093 return builtURL; 44094 }, 44095 44096 // build an absolute path using the provided basePath 44097 // adapted from https://developer.mozilla.org/en-US/docs/Web/API/document/cookie#Using_relative_URLs_in_the_path_parameter 44098 // this does not handle the case where relativePath is "/" or "//". These
44098cases should be handled outside this. 44099 buildAbsolutePath: function(basePath, relativePath) { 44100 var sRelPath = relativePath; 44101 var nUpLn, sDir = '', sPath = basePath.replace(/[^\/]*$/, sRelPath.replace(/(\/|^)(?:\.?\/+)+/g, '$1')); 44102 for (var nEnd, nStart = 0; nEnd = sPath.indexOf('/../', nStart), nEnd > -1; nStart = nEnd + nUpLn) { 44103 nUpLn = /^\/(?:\.\.\/)*/.exec(sPath.slice(nEnd))[0].length; 44104 sDir = (sDir + sPath.substring(nStart, nEnd)).replace(new RegExp('(?:\\\/+[^\\\/]*){0,' + ((nUpLn - 1) / 3) + '}$'), '/'); 44105 } 44106 return sDir + sPath.substr(nStart); 44107 } 44108 }; 44109 44110/* jshint ignore:start */ 44111 if(typeof exports === 'object' && typeof module === 'object') 44112 module.exports = URLToolkit; 44113 else if(typeof define === 'function' && define.amd) 44114 define([], function() { return URLToolkit; }); 44115 else if(typeof exports === 'object') 44116 exports["URLToolkit"] = URLToolkit; 44117 else 44118 root["URLToolkit"] = URLToolkit; 44119})(this); 44120/* jshint ignore:end */ 44121 44122},{}],64:[function(require,module,exports){ 44123(function (global){ 44124/** 44125 * @file add-text-track-data.js 44126 */ 44127'use strict'; 44128 44129Object.defineProperty(exports, '__esModule', { 44130 value: true 44131}); 44132 44133function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 44134 44135var _globalWindow = require('global/window'); 44136 44137var _globalWindow2 = _interopRequireDefault(_globalWindow); 44138 44139var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null); 44140 44141var _videoJs2 = _interopRequireDefault(_videoJs); 44142 44143/** 44144 * Define properties on a cue for backwards compatability, 44145 * but warn the user that the way that they are using it 44146 * is depricated and will be removed at a later date. 44147 * 44148 * @param {Cue} cue the cue to add the properties on 44149 * @private 44150 */ 44151var deprecateOldCue = function deprecateOldCue(cue) { 44152 Object.defineProperties(cue.frame, { 44153 id: { 44154 get: function get() { 44155 _videoJs2['default'].log.warn('cue.frame.id is deprecated. Use cue.value.key instead.'); 44156 return cue.value.key; 44157 } 44158 }, 44159 value: { 44160 get: function get() { 44161 _videoJs2['default'].log.warn('cue.frame.value is deprecated. Use cue.value.data instead.'); 44162 return cue.value.data; 44163 } 44164 }, 44165 privateData: { 44166 get: function get() { 44167 _videoJs2['default'].log.warn('cue.frame.privateData is deprecated. Use cue.value.data instead.'); 44168 return cue.value.data; 44169 } 44170 } 44171 }); 44172}; 44173 44174var durationOfVideo = function durationOfVideo(duration) { 44175 var dur = undefined; 44176 44177 if (isNaN(duration) || Math.abs(duration) === Infinity) { 44178 dur = Number.MAX_VALUE; 44179 } else { 44180 dur = duration; 44181 } 44182 return dur; 44183}; 44184/** 44185 * Add text track data to a source handler given the captions and 44186 * metadata from the buffer. 44187 * 44188 * @param {Object} sourceHandler the flash or virtual source buffer 44189 * @param {Array} captionArray an array of caption data 44190 * @param {Array} metadataArray an array of meta data 44191 * @private 44192 */ 44193var addTextTrackData = function addTextTrackData(sourceHandler, captionArray, metadataArray) { 44194 var Cue = _globalWindow2['default'].WebKitDataCue || _globalWindow2['default'].VTTCue; 44195 44196 if (captionArray) { 44197 captionArray.forEach(function (caption) { 44198 var track = caption.stream; 44199 44200 this.inbandTextTracks_[track].addCue(new Cue(caption.startTime + this.timestampOffset, caption.endTime + this.timestampOffset, caption.text)); 44201 }, sourceHandler); 44202 } 44203 44204 if (metadataArray) { 44205 (function () { 44206 var videoDuration = durationOfVideo(sourceHandler.mediaSource_.duration); 44207 44208 metadataArray.forEach(function (metadata) { 44209 var time = metadata.cueTime + this.timestampOffset; 44210 44211 metadata.frames.forEach(function (frame) { 44212 var cue = new Cue(time, time, frame.value || frame.url || frame.data || ''); 44213 44214 cue.frame = frame; 44215 cue.value = frame; 44216 deprecateOldCue(cue); 44217 44218 this.metadataTrack_.addCue(cue); 44219 }, this); 44220 }, sourceHandler); 44221 44222 // Updating the metadeta cues so that 44223 // the endTime of each cue is the startTime of the next cue 44224 // the endTime of last cue is the duration of the video 44225 if (sourceHandler.metadataTrack_ && sourceHandler.metadataTrack_.cues && sourceHandler.metadataTrack_.cues.length) { 44226 (function () { 44227 var cues = sourceHandler.metadataTrack_.cues; 44228 var cuesArray = []; 44229 44230 // Create a copy of the TextTrackCueList... 44231 // ...disregarding cues with a falsey value 44232 for (var i = 0; i < cues.length; i++) { 44233 if (cues[i]) { 44234 cuesArray.push(cues[i]); 44235 } 44236 } 44237 44238 // Group cues by their startTime value 44239 var cuesGroupedByStartTime = cuesArray.reduce(function (obj, cue) { 44240 var timeSlot = obj[cue.startTime] || []; 44241 44242 timeSlot.push(cue); 44243 obj[cue.startTime] = timeSlot; 44244 44245 return obj; 44246 }, {}); 44247 44248 // Sort startTimes by ascending order 44249 var sortedStartTimes = Object.keys(cuesGroupedByStartTime).sort(function (a, b) { 44250 return Number(a) - Number(b); 44251 }); 44252 44253 // Map each cue group's endTime to the next group's startTime
44254 sortedStartTimes.forEach(function (startTime, idx) { 44255 var cueGroup = cuesGroupedByStartTime[startTime]; 44256 var nextTime = Number(sortedStartTimes[idx + 1]) || videoDuration; 44257 44258 // Map each cue's endTime the next group's startTime 44259 cueGroup.forEach(function (cue) { 44260 cue.endTime = nextTime; 44261 }); 44262 }); 44263 })(); 44264 } 44265 })(); 44266 } 44267}; 44268 44269exports['default'] = { 44270 addTextTrackData: addTextTrackData, 44271 durationOfVideo: durationOfVideo 44272}; 44273module.exports = exports['default']; 44274}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 44275},{"global/window":32}],65:[function(require,module,exports){ 44276/** 44277 * @file codec-utils.js 44278 */ 44279 44280/** 44281 * Check if a codec string refers to an audio codec. 44282 * 44283 * @param {String} codec codec string to check 44284 * @return {Boolean} if this is an audio codec 44285 * @private 44286 */ 44287'use strict'; 44288 44289Object.defineProperty(exports, '__esModule', { 44290 value: true 44291}); 44292var isAudioCodec = function isAudioCodec(codec) { 44293 return (/mp4a\.\d+.\d+/i.test(codec) 44294 ); 44295}; 44296 44297/** 44298 * Check if a codec string refers to a video codec. 44299 * 44300 * @param {String} codec codec string to check 44301 * @return {Boolean} if this is a video codec 44302 * @private 44303 */ 44304var isVideoCodec = function isVideoCodec(codec) { 44305 return (/avc1\.[\da-f]+/i.test(codec) 44306 ); 44307}; 44308 44309/** 44310 * Parse a content type header into a type and parameters 44311 * object 44312 * 44313 * @param {String} type the content type header 44314 * @return {Object} the parsed content-type 44315 * @private 44316 */ 44317var parseContentType = function parseContentType(type) { 44318 var object = { type: '', parameters: {} }; 44319 var parameters = type.trim().split(';'); 44320 44321 // first parameter should always be content-type 44322 object.type = parameters.shift().trim(); 44323 parameters.forEach(function (parameter) { 44324 var pair = parameter.trim().split('='); 44325 44326 if (pair.length > 1) { 44327 var _name = pair[0].replace(/"/g, '').trim(); 44328 var value = pair[1].replace(/"/g, '').trim(); 44329 44330 object.parameters[_name] = value; 44331 } 44332 }); 44333 44334 return object; 44335}; 44336 44337/** 44338 * Replace the old apple-style `avc1.<dd>.<dd>` codec string with the standard 44339 * `avc1.<hhhhhh>` 44340 * 44341 * @param {Array} codecs an array of codec strings to fix 44342 * @return {Array} the translated codec array 44343 * @private 44344 */ 44345var translateLegacyCodecs = function translateLegacyCodecs(codecs) { 44346 return codecs.map(function (codec) { 44347 return codec.replace(/avc1\.(\d+)\.(\d+)/i, function (orig, profile, avcLevel) { 44348 var profileHex = ('00' + Number(profile).toString(16)).slice(-2); 44349 var avcLevelHex = ('00' + Number(avcLevel).toString(16)).slice(-2); 44350 44351 return 'avc1.' + profileHex + '00' + avcLevelHex; 44352 }); 44353 }); 44354}; 44355 44356exports['default'] = { 44357 isAudioCodec: isAudioCodec, 44358 parseContentType: parseContentType, 44359 isVideoCodec: isVideoCodec, 44360 translateLegacyCodecs: translateLegacyCodecs 44361}; 44362module.exports = exports['default']; 44363},{}],66:[function(require,module,exports){ 44364/** 44365 * @file create-text-tracks-if-necessary.js 44366 */ 44367 44368/** 44369 * Create text tracks on video.js if they exist on a segment. 44370 * 44371 * @param {Object} sourceBuffer the VSB or FSB 44372 * @param {Object} mediaSource the HTML or Flash media source 44373 * @param {Object} segment the segment that may contain the text track 44374 * @private 44375 */ 44376'use strict'; 44377 44378Object.defineProperty(exports, '__esModule', { 44379 value: true 44380}); 44381var createTextTracksIfNecessary = function createTextTracksIfNecessary(sourceBuffer, mediaSource, segment) { 44382 var player = mediaSource.player_; 44383 44384 // create an in-band caption track if one is present in the segment 44385 if (segment.captions && segment.captions.length) { 44386 if (!sourceBuffer.inbandTextTracks_) { 44387 sourceBuffer.inbandTextTracks_ = {}; 44388 } 44389 44390 for (var trackId in segment.captionStreams) { 44391 if (!sourceBuffer.inbandTextTracks_[trackId]) { 44392 player.tech_.trigger({ type: 'usage', name: 'hls-608' }); 44393 var track = player.textTracks().getTrackById(trackId); 44394 44395 if (track) { 44396 // Resuse an existing track with a CC# id because this was 44397 // very likely created by videojs-contrib-hls from information 44398 // in the m3u8 for us to use 44399 sourceBuffer.inbandTextTracks_[trackId] = track; 44400 } else { 44401 // Otherwise, create a track with the default `CC#` label and 44402 // without a language 44403 sourceBuffer.inbandTextTracks_[trackId] = player.addRemoteTextTrack({ 44404 kind: 'captions', 44405 id: trackId, 44406 label: trackId 44407 }, false).track; 44408 } 44409 } 44410 } 44411 } 44412 44413 if (segment.metadata && segment.metadata.length && !sourceBuffer.metadataTrack_) { 44414 sourceBuffer.metadataTrack_ = player.addRemoteTextTrack({ 44415 kind: 'metadata', 44416 label: 'Timed Metadata' 44417 }, false).track; 44418 sourceBuffer.metadataTrack_.inBandMetadataTrackDispatchType = segment.metadata.dispatchType; 44419 } 44420}; 44421 44422exports['default'] = createTextTracksIfNecessary; 44423module.exports = exports['default']; 44424},{}],67:[function(require,module,exports){ 44425/** 44426 * @file flash-constants.js 44427 */ 44428/** 44429 * The maximum size in bytes for append operations to the video.js 44430 * SWF. Calling through to Flash blocks and can be expensive so 44431 * we chunk data and pass through 4KB at a time, yielding to the 44432 * browser between chunks. This gives a theoretical maximum rate of 44433 * 1MB/s into Flash. Any higher and we begin to drop frames and UI 44434 * responsiveness suffers. 44435 * 44436 * @private 44437 */ 44438"use strict"; 44439 44440Object.defineProperty(exports, "__esModule", { 44441 value: true 44442}); 44443var flashConstants = { 44444 // times in milliseconds 44445 TIME_BETWEEN_CHUNKS: 1, 44446 BYTES_PER_CHUNK: 1024 * 32 44447}; 44448 44449exports["default"] = flashConstants; 44450module.exports = exports["default"]; 44451},{}],68:[function(require,module,exports){ 44452(function (global){ 44453/** 44454 * @file flash-media-source.js 44455 */ 44456'use strict'; 44457 44458Object.defineProperty(exports, '__esModule', { 44459 value: true 44460}); 44461 44462var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 44463 44464var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 44465 44466function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 44467 44468function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 44469 44470function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
44470onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 44471 44472var _globalDocument = require('global/document'); 44473 44474var _globalDocument2 = _interopRequireDefault(_globalDocument); 44475 44476var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null); 44477 44478var _videoJs2 = _interopRequireDefault(_videoJs); 44479 44480var _flashSourceBuffer = require('./flash-source-buffer'); 44481 44482var _flashSourceBuffer2 = _interopRequireDefault(_flashSourceBuffer); 44483 44484var _flashConstants = require('./flash-constants'); 44485 44486var _flashConstants2 = _interopRequireDefault(_flashConstants); 44487 44488var _codecUtils = require('./codec-utils'); 44489 44490/** 44491 * A flash implmentation of HTML MediaSources and a polyfill 44492 * for browsers that don't support native or HTML MediaSources.. 44493 * 44494 * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource 44495 * @class FlashMediaSource 44496 * @extends videojs.EventTarget 44497 */ 44498 44499var FlashMediaSource = (function (_videojs$EventTarget) { 44500 _inherits(FlashMediaSource, _videojs$EventTarget); 44501 44502 function FlashMediaSource() { 44503 var _this = this; 44504 44505 _classCallCheck(this, FlashMediaSource); 44506 44507 _get(Object.getPrototypeOf(FlashMediaSource.prototype), 'constructor', this).call(this); 44508 this.sourceBuffers = []; 44509 this.readyState = 'closed'; 44510 44511 this.on(['sourceopen', 'webkitsourceopen'], function (event) { 44512 // find the swf where we will push media data 44513 _this.swfObj = _globalDocument2['default'].getElementById(event.swfId); 44514 _this.player_ = (0, _videoJs2['default'])(_this.swfObj.parentNode); 44515 _this.tech_ = _this.swfObj.tech; 44516 _this.readyState = 'open'; 44517 44518 _this.tech_.on('seeking', function () { 44519 var i = _this.sourceBuffers.length; 44520 44521 while (i--) { 44522 _this.sourceBuffers[i].abort(); 44523 } 44524 }); 44525 44526 // trigger load events 44527 if (_this.swfObj) { 44528 _this.swfObj.vjs_load(); 44529 } 44530 }); 44531 } 44532 44533 /** 44534 * Set or return the presentation duration. 44535 * 44536 * @param {Double} value the duration of the media in seconds 44537 * @param {Double} the current presentation duration 44538 * @link http://www.w3.org/TR/media-source/#widl-MediaSource-duration 44539 */ 44540 44541 /** 44542 * We have this function so that the html and flash interfaces 44543 * are the same. 44544 * 44545 * @private 44546 */ 44547 44548 _createClass(FlashMediaSource, [{ 44549 key: 'addSeekableRange_', 44550 value: function addSeekableRange_() {} 44551 // intentional no-op 44552 44553 /** 44554 * Create a new flash source buffer and add it to our flash media source. 44555 * 44556 * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/addSourceBuffer 44557 * @param {String} type the content-type of the source 44558 * @return {Object} the flash source buffer 44559 */ 44560 44561 }, { 44562 key: 'addSourceBuffer', 44563 value: function addSourceBuffer(type) { 44564 var parsedType = (0, _codecUtils.parseContentType)(type); 44565 var sourceBuffer = undefined; 44566 44567 // if this is an FLV type, we'll push data to flash 44568 if (parsedType.type === 'video/mp2t' || parsedType.type === 'audio/mp2t') { 44569 // Flash source buffers 44570 sourceBuffer = new _flashSourceBuffer2['default'](this); 44571 } else { 44572 throw new Error('NotSupportedError (Video.js)'); 44573 } 44574 44575 this.sourceBuffers.push(sourceBuffer); 44576 return sourceBuffer; 44577 } 44578 44579 /** 44580 * Signals the end of the stream. 44581 * 44582 * @link https://w3c.github.io/media-source/#widl-MediaSource-endOfStream-void-EndOfStreamError-error 44583 * @param {String=} error Signals that a playback error 44584 * has occurred. If specified, it must be either "network" or 44585 * "decode". 44586 */ 44587 }, { 44588 key: 'endOfStream', 44589 value: function endOfStream(error) { 44590 if (error === 'network') { 44591 // MEDIA_ERR_NETWORK 44592 this.tech_.error(2); 44593 } else if (error === 'decode') { 44594 // MEDIA_ERR_DECODE 44595 this.tech_.error(3); 44596 } 44597 if (this.readyState !== 'ended') { 44598 this.readyState = 'ended'; 44599 this.swfObj.vjs_endOfStream(); 44600 } 44601 } 44602 }]); 44603 44604 return FlashMediaSource; 44605})(_videoJs2['default'].EventTarget); 44606 44607exports['default'] = FlashMediaSource; 44608try { 44609 Object.defineProperty(FlashMediaSource.prototype, 'duration', { 44610 /** 44611 * Return the presentation duration. 44612 * 44613 * @return {Double} the duration of the media in seconds 44614 * @link http://www.w3.org/TR/media-source/#widl-MediaSource-duration 44615 */ 44616 get: function get() { 44617 if (!this.swfObj) { 44618 return NaN; 44619 } 44620 // get the current duration from the SWF 44621 return this.swfObj.vjs_getProperty('duration'); 44622 }, 44623 /** 44624 * Set the presentation duration. 44625 * 44626 * @param {Double} value the duration of the media in seconds 44627 * @return {Double} the duration of the media in seconds 44628 * @link http://www.w3.org/TR/media-source/#widl-MediaSource-duration 44629 */ 44630 set: function set(value) { 44631 var i = undefined; 44632 var oldDuration = this.swfObj.vjs_getProperty('duration'); 44633 44634 this.swfObj.vjs_setProperty('duration', value); 44635 44636 if (value < oldDuration) { 44637 // In MSE, this triggers the range removal algorithm which causes 44638 // an update to occur 44639 for (i = 0; i < this.sourceBuffers.length; i++) { 44640 this.sourceBuffers[i].remove(value, oldDuration); 44641 } 44642 } 44643 44644 return value; 44645 } 44646 }); 44647} catch (e) { 44648 // IE8 throws if defineProperty is called on a non-DOM node. We 44649 // don't support IE8 but we shouldn't throw an error if loaded 44650 // there. 44651 FlashMediaSource.prototype.duration = NaN; 44652} 44653 44654for (var property in _flashConstants2['default']) { 44655 FlashMediaSource[property] = _flashConstants2['default'][property]; 44656} 44657module.exports = exports['default']; 44658}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 44659},{"./codec-utils":65,"./flash-constants":67,"./flash-source-buffer":69,"global/document":31}],69:[function(require,module,exports){ 44660(function (global){ 44661/** 44662 * @file flash-source-buffer.js 44663 */ 44664'use strict'; 44665 44666Object.defineProperty(exports, '__esModule', { 44667 value: true 44668}); 44669 44670var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 44671 44672var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 44673 44674function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 44675 44676function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 44677 44678function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
44678onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 44679 44680var _globalWindow = require('global/window'); 44681 44682var _globalWindow2 = _interopRequireDefault(_globalWindow); 44683 44684var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null); 44685 44686var _videoJs2 = _interopRequireDefault(_videoJs); 44687 44688var _muxJsLibFlv = require('mux.js/lib/flv'); 44689 44690var _muxJsLibFlv2 = _interopRequireDefault(_muxJsLibFlv); 44691 44692var _removeCuesFromTrack = require('./remove-cues-from-track'); 44693 44694var _removeCuesFromTrack2 = _interopRequireDefault(_removeCuesFromTrack); 44695 44696var _createTextTracksIfNecessary = require('./create-text-tracks-if-necessary'); 44697 44698var _createTextTracksIfNecessary2 = _interopRequireDefault(_createTextTracksIfNecessary); 44699 44700var _addTextTrackData = require('./add-text-track-data'); 44701 44702var _flashTransmuxerWorker = require('./flash-transmuxer-worker'); 44703 44704var _flashTransmuxerWorker2 = _interopRequireDefault(_flashTransmuxerWorker); 44705 44706var _webworkify = require('webworkify'); 44707 44708var _webworkify2 = _interopRequireDefault(_webworkify); 44709 44710var _flashConstants = require('./flash-constants'); 44711 44712var _flashConstants2 = _interopRequireDefault(_flashConstants); 44713 44714/** 44715 * A wrapper around the setTimeout function that uses 44716 * the flash constant time between ticks value. 44717 * 44718 * @param {Function} func the function callback to run 44719 * @private 44720 */ 44721var scheduleTick = function scheduleTick(func) { 44722 // Chrome doesn't invoke requestAnimationFrame callbacks 44723 // in background tabs, so use setTimeout. 44724 _globalWindow2['default'].setTimeout(func, _flashConstants2['default'].TIME_BETWEEN_CHUNKS); 44725}; 44726 44727/** 44728 * Generates a random string of max length 6 44729 * 44730 * @return {String} the randomly generated string 44731 * @function generateRandomString 44732 * @private 44733 */ 44734var generateRandomString = function generateRandomString() { 44735 return Math.random().toString(36).slice(2, 8); 44736}; 44737 44738/** 44739 * Round a number to a specified number of places much like 44740 * toFixed but return a number instead of a string representation. 44741 * 44742 * @param {Number} num A number 44743 * @param {Number} places The number of decimal places which to 44744 * round 44745 * @private 44746 */ 44747var toDecimalPlaces = function toDecimalPlaces(num, places) { 44748 if (typeof places !== 'number' || places < 0) { 44749 places = 0; 44750 } 44751 44752 var scale = Math.pow(10, places); 44753 44754 return Math.round(num * scale) / scale; 44755}; 44756 44757/** 44758 * A SourceBuffer implementation for Flash rather than HTML. 44759 * 44760 * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource 44761 * @param {Object} mediaSource the flash media source 44762 * @class FlashSourceBuffer 44763 * @extends videojs.EventTarget 44764 */ 44765 44766var FlashSourceBuffer = (function (_videojs$EventTarget) { 44767 _inherits(FlashSourceBuffer, _videojs$EventTarget); 44768 44769 function FlashSourceBuffer(mediaSource) { 44770 var _this = this; 44771 44772 _classCallCheck(this, FlashSourceBuffer); 44773 44774 _get(Object.getPrototypeOf(FlashSourceBuffer.prototype), 'constructor', this).call(this); 44775 var encodedHeader = undefined; 44776 44777 // Start off using the globally defined value but refine 44778 // as we append data into flash 44779 this.chunkSize_ = _flashConstants2['default'].BYTES_PER_CHUNK; 44780 44781 // byte arrays queued to be appended 44782 this.buffer_ = []; 44783 44784 // the total number of queued bytes 44785 this.bufferSize_ = 0; 44786 44787 // to be able to determine the correct position to seek to, we 44788 // need to retain information about the mapping between the 44789 // media timeline and PTS values 44790 this.basePtsOffset_ = NaN; 44791 44792 this.mediaSource_ = mediaSource; 44793 44794 this.audioBufferEnd_ = NaN; 44795 this.videoBufferEnd_ = NaN; 44796 44797 // indicates whether the asynchronous continuation of an operation 44798 // is still being processed 44799 // see https://w3c.github.io/media-source/#widl-SourceBuffer-updating 44800 this.updating = false; 44801 this.timestampOffset_ = 0; 44802 44803 encodedHeader = _globalWindow2['default'].btoa(String.fromCharCode.apply(null, Array.prototype.slice.call(_muxJsLibFlv2['default'].getFlvHeader()))); 44804 44805 // create function names with added randomness for the global callbacks flash will use 44806 // to get data from javascript into the swf. Random strings are added as a safety 44807 // measure for pages with multiple players since these functions will be global 44808 // instead of per instance. When making a call to the swf, the browser generates a 44809 // try catch code snippet, but just takes the function name and writes out an unquoted 44810 // call to that function. If the player id has any special characters, this will result 44811 // in an error, so safePlayerId replaces all special characters to '_' 44812 var safePlayerId = this.mediaSource_.player_.id().replace(/[^a-zA-Z0-9]/g, '_'); 44813 44814 this.flashEncodedHeaderName_ = 'vjs_flashEncodedHeader_' + s
44814afePlayerId + generateRandomString(); 44815 this.flashEncodedDataName_ = 'vjs_flashEncodedData_' + safePlayerId + generateRandomString(); 44816 44817 _globalWindow2['default'][this.flashEncodedHeaderName_] = function () { 44818 delete _globalWindow2['default'][_this.flashEncodedHeaderName_]; 44819 return encodedHeader; 44820 }; 44821 44822 this.mediaSource_.swfObj.vjs_appendChunkReady(this.flashEncodedHeaderName_); 44823 44824 this.transmuxer_ = (0, _webworkify2['default'])(_flashTransmuxerWorker2['default']); 44825 this.transmuxer_.postMessage({ action: 'init', options: {} }); 44826 this.transmuxer_.onmessage = function (event) { 44827 if (event.data.action === 'data') { 44828 _this.receiveBuffer_(event.data.segment); 44829 } 44830 }; 44831 44832 this.one('updateend', function () { 44833 _this.mediaSource_.tech_.trigger('loadedmetadata'); 44834 }); 44835 44836 Object.defineProperty(this, 'timestampOffset', { 44837 get: function get() { 44838 return this.timestampOffset_; 44839 }, 44840 set: function set(val) { 44841 if (typeof val === 'number' && val >= 0) { 44842 this.timestampOffset_ = val; 44843 // We have to tell flash to expect a discontinuity 44844 this.mediaSource_.swfObj.vjs_discontinuity(); 44845 // the media <-> PTS mapping must be re-established after 44846 // the discontinuity 44847 this.basePtsOffset_ = NaN; 44848 this.audioBufferEnd_ = NaN; 44849 this.videoBufferEnd_ = NaN; 44850 44851 this.transmuxer_.postMessage({ action: 'reset' }); 44852 } 44853 } 44854 }); 44855 44856 Object.defineProperty(this, 'buffered', { 44857 get: function get() { 44858 if (!this.mediaSource_ || !this.mediaSource_.swfObj || !('vjs_getProperty' in this.mediaSource_.swfObj)) { 44859 return _videoJs2['default'].createTimeRange(); 44860 } 44861 44862 var buffered = this.mediaSource_.swfObj.vjs_getProperty('buffered'); 44863 44864 if (buffered && buffered.length) { 44865 buffered[0][0] = toDecimalPlaces(buffered[0][0], 3); 44866 buffered[0][1] = toDecimalPlaces(buffered[0][1], 3); 44867 } 44868 return _videoJs2['default'].createTimeRanges(buffered); 44869 } 44870 }); 44871 44872 // On a seek we remove all text track data since flash has no concept 44873 // of a buffered-range and everything else is reset on seek 44874 this.mediaSource_.player_.on('seeked', function () { 44875 (0, _removeCuesFromTrack2['default'])(0, Infinity, _this.metadataTrack_); 44876 if (_this.inbandTextTracks_) { 44877 for (var track in _this.inbandTextTracks_) { 44878 (0, _removeCuesFromTrack2['default'])(0, Infinity, _this.inbandTextTracks_[track]); 44879 } 44880 } 44881 }); 44882 44883 var onHlsReset = this.onHlsReset_.bind(this); 44884 44885 this.mediaSource_.player_.tech_.on('hls-reset', onHlsReset); 44886 44887 this.mediaSource_.player_.tech_.hls.on('dispose', function () { 44888 _this.transmuxer_.terminate(); 44889 _this.mediaSource_.player_.tech_.off('hls-reset', onHlsReset); 44890 }); 44891 } 44892 44893 /** 44894 * Append bytes to the sourcebuffers buffer, in this case we 44895 * have to append it to swf object. 44896 * 44897 * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/appendBuffer 44898 * @param {Array} bytes 44899 */ 44900 44901 _createClass(FlashSourceBuffer, [{ 44902 key: 'appendBuffer', 44903 value: function appendBuffer(bytes) { 44904 var error = undefined; 44905 44906 if (this.updating) { 44907 error = new Error('SourceBuffer.append() cannot be called ' + 'while an update is in progress'); 44908 error.name = 'InvalidStateError'; 44909 error.code = 11; 44910 throw error; 44911 } 44912 this.updating = true; 44913 this.mediaSource_.readyState = 'open'; 44914 this.trigger({ type: 'update' }); 44915 44916 this.transmuxer_.postMessage({ 44917 action: 'push', 44918 data: bytes.buffer, 44919 byteOffset: bytes.byteOffset, 44920 byteLength: bytes.byteLength 44921 }, [bytes.buffer]); 44922 this.transmuxer_.postMessage({ action: 'flush' }); 44923 } 44924 44925 /** 44926 * Reset the parser and remove any data queued to be sent to the SWF. 44927 * 44928 * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/abort 44929 */ 44930 }, { 44931 key: 'abort', 44932 value: function abort() { 44933 this.buffer_ = []; 44934 this.bufferSize_ = 0; 44935 this.mediaSource_.swfObj.vjs_abort(); 44936 44937 // report any outstanding updates have ended 44938 if (this.updating) { 44939 this.updating = false;
44940 this.trigger({ type: 'updateend' }); 44941 } 44942 } 44943 44944 /** 44945 * Flash cannot remove ranges already buffered in the NetStream 44946 * but seeking clears the buffer entirely. For most purposes, 44947 * having this operation act as a no-op is acceptable. 44948 * 44949 * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/remove 44950 * @param {Double} start start of the section to remove 44951 * @param {Double} end end of the section to remove 44952 */ 44953 }, { 44954 key: 'remove', 44955 value: function remove(start, end) { 44956 (0, _removeCuesFromTrack2['default'])(start, end, this.metadataTrack_); 44957 if (this.inbandTextTracks_) { 44958 for (var track in this.inbandTextTracks_) { 44959 (0, _removeCuesFromTrack2['default'])(start, end, this.inbandTextTracks_[track]); 44960 } 44961 } 44962 this.trigger({ type: 'update' }); 44963 this.trigger({ type: 'updateend' }); 44964 } 44965 44966 /** 44967 * Receive a buffer from the flv. 44968 * 44969 * @param {Object} segment 44970 * @private 44971 */ 44972 }, { 44973 key: 'receiveBuffer_', 44974 value: function receiveBuffer_(segment) { 44975 var _this2 = this; 44976 44977 // create an in-band caption track if one is present in the segment 44978 (0, _createTextTracksIfNecessary2['default'])(this, this.mediaSource_, segment); 44979 (0, _addTextTrackData.addTextTrackData)(this, segment.captions, segment.metadata); 44980 44981 // Do this asynchronously since convertTagsToData_ can be time consuming 44982 scheduleTick(function () { 44983 var flvBytes = _this2.convertTagsToData_(segment); 44984 44985 if (_this2.buffer_.length === 0) { 44986 scheduleTick(_this2.processBuffer_.bind(_this2)); 44987 } 44988 44989 if (flvBytes) { 44990 _this2.buffer_.push(flvBytes); 44991 _this2.bufferSize_ += flvBytes.byteLength; 44992 } 44993 }); 44994 } 44995 44996 /** 44997 * Append a portion of the current buffer to the SWF. 44998 * 44999 * @private 45000 */ 45001 }, { 45002 key: 'processBuffer_', 45003 value: function processBuffer_() { 45004 var _this3 = this; 45005 45006 var chunkSize = _flashConstants2['default'].BYTES_PER_CHUNK; 45007 45008 if (!this.buffer_.length) { 45009 if (this.updating !== false) { 45010 this.updating = false; 45011 this.trigger({ type: 'updateend' }); 45012 } 45013 // do nothing if the buffer is empty 45014 return; 45015 } 45016 45017 // concatenate appends up to the max append size 45018 var chunk = this.buffer_[0].subarray(0, chunkSize); 45019 45020 // requeue any bytes that won't make it this round 45021 if (chunk.byteLength < chunkSize || this.buffer_[0].byteLength === chunkSize) { 45022 this.buffer_.shift(); 45023 } else { 45024 this.buffer_[0] = this.buffer_[0].subarray(chunkSize); 45025 } 45026 45027 this.bufferSize_ -= chunk.byteLength; 45028 45029 // base64 encode the bytes 45030 var binary = []; 45031 var length = chunk.byteLength; 45032 45033 for (var i = 0; i < length; i++) { 45034 binary.push(String.fromCharCode(chunk[i])); 45035 } 45036 var b64str = _globalWindow2['default'].btoa(binary.join('')); 45037 45038 _globalWindow2['default'][this.flashEncodedDataName_] = function () { 45039 // schedule another processBuffer to process any left over data or to 45040 // trigger updateend 45041 scheduleTick(_this3.processBuffer_.bind(_this3)); 45042 delete _globalWindow2['default'][_this3.flashEncodedDataName_]; 45043 return b64str; 45044 }; 45045 45046 // Notify the swf that segment data is ready to be appended 45047 this.mediaSource_.swfObj.vjs_appendChunkReady(this.flashEncodedDataName_); 45048 } 45049 45050 /** 45051 * Turns an array of flv tags into a Uint8Array representing the 45052 * flv data. Also removes any tags that are before the current 45053 * time so that playback begins at or slightly after the right 45054 * place on a seek 45055 * 45056 * @private 45057 * @param {Object} segmentData object of segment data 45058 */ 45059 }, { 45060 key: 'convertTagsToData_', 45061 value: function convertTagsToData_(segmentData) { 45062 var segmentByteLength = 0; 45063 var tech = this.mediaSource_.tech_; 45064 var videoTargetPts = 0; 45065 var segment = undefined; 45066 var videoTags = segmentData.tags.videoTags; 45067 var audioTags = segmentData.tags.audioTags; 45068 45069 // Establish the media timeline to PTS translation if we don't 45070 // have one already 45071 if (isNaN(this.basePtsOffset_) && (videoTags.length || audioTags.length)) { 45072 // We know there is at least one video or audio tag, but since we may
45072not have both, 45073 // we use pts: Infinity for the missing tag. The will force the following Math.min 45074 // call will to use the proper pts value since it will always be less than Infinity 45075 var firstVideoTag = videoTags[0] || { pts: Infinity }; 45076 var firstAudioTag = audioTags[0] || { pts: Infinity }; 45077 45078 this.basePtsOffset_ = Math.min(firstAudioTag.pts, firstVideoTag.pts); 45079 } 45080 45081 if (tech.seeking()) { 45082 // Do not use previously saved buffer end values while seeking since buffer 45083 // is cleared on all seeks 45084 this.videoBufferEnd_ = NaN; 45085 this.audioBufferEnd_ = NaN; 45086 } 45087 45088 if (isNaN(this.videoBufferEnd_)) { 45089 if (tech.buffered().length) { 45090 videoTargetPts = tech.buffered().end(0) - this.timestampOffset; 45091 } 45092 45093 // Trim to currentTime if seeking 45094 if (tech.seeking()) { 45095 videoTargetPts = Math.max(videoTargetPts, tech.currentTime() - this.timestampOffset); 45096 } 45097 45098 // PTS values are represented in milliseconds 45099 videoTargetPts *= 1e3; 45100 videoTargetPts += this.basePtsOffset_; 45101 } else { 45102 // Add a fudge factor of 0.1 to the last video pts appended since a rendition change 45103 // could append an overlapping segment, in which case there is a high likelyhood 45104 // a tag could have a matching pts to videoBufferEnd_, which would cause 45105 // that tag to get appended by the tag.pts >= targetPts check below even though it 45106 // is a duplicate of what was previously appended 45107 videoTargetPts = this.videoBufferEnd_ + 0.1; 45108 } 45109 45110 // filter complete GOPs with a presentation time less than the seek target/end of buffer 45111 var currentIndex = videoTags.length; 45112 45113 // if the last tag is beyond videoTargetPts, then do not search the list for a GOP 45114 // since our videoTargetPts lies in a future segment 45115 if (currentIndex && videoTags[currentIndex - 1].pts >= videoTargetPts) { 45116 // Start by walking backwards from the end of the list until we reach a tag that 45117 // is equal to or less than videoTargetPts 45118 while (--currentIndex) { 45119 var currentTag = videoTags[currentIndex]; 45120 45121 if (currentTag.pts > videoTargetPts) { 45122 continue; 45123 } 45124 45125 // if we see a keyFrame or metadata tag once we've gone below videoTargetPts, 45126 // exit the loop as this is the start of the GOP that we want to append 45127 if (currentTag.keyFrame || currentTag.metaDataTag) { 45128 break; 45129 } 45130 } 45131 45132 // We need to check if there are any metadata tags that come before currentIndex 45133 // as those will be metadata tags associated with the GOP we are appending 45134 // There could be 0 to 2 metadata tags that come before the currentIndex depending 45135 // on what videoTargetPts is and whether the transmuxer prepended metadata tags to this 45136 // key frame 45137 while (currentIndex) { 45138 var nextTag = videoTags[currentIndex - 1]; 45139 45140 if (!nextTag.metaDataTag) { 45141 break; 45142 } 45143 45144 currentIndex--; 45145 } 45146 } 45147 45148 var filteredVideoTags = videoTags.slice(currentIndex); 45149 45150 var audioTargetPts = undefined; 45151 45152 if (isNaN(this.audioBufferEnd_)) { 45153 audioTargetPts = videoTargetPts; 45154 } else { 45155 // Add a fudge factor of 0.1 to the last video pts appended since a rendition change 45156 // could append an overlapping segment, in which case there is a high likelyhood 45157 // a tag could have a matching pts to videoBufferEnd_, which would cause 45158 // that tag to get appended by the tag.pts >= targetPts check below even though it 45159 // is a duplicate of what was previously appended 45160 audioTargetPts = this.audioBufferEnd_ + 0.1; 45161 } 45162 45163 if (filteredVideoTags.length) { 45164 // If targetPts intersects a GOP and we appended the tags for the GOP that came 45165 // before targetPts, we want to make sure to trim audio tags at the pts 45166 // of the first video tag to avoid brief moments of silence 45167 audioTargetPts = Math.min(audioTargetPts, filteredVideoTags[0].pts); 45168 } 45169 45170 // skip tags with a presentation time less than the seek target/end of buffer 45171 currentIndex = 0; 45172 45173 while (currentIndex < audioTags.length) { 45174 if (audioTags[currentIndex].pts >= audioTargetPts) { 45175 break; 45176 } 45177 45178 currentIndex++; 45179 } 45180 45181 var filteredAudioTags = audioTags.slice(currentIndex); 45182 45183 // update the audio and video buffer ends 45184 if (filteredAudioTags.length) { 45185 this.audioBufferEnd_ = filteredAudioTags[filteredAudioTags.length - 1].pts; 45186 } 45187 if (filteredVideoTags.length) { 45188 this.videoBufferEnd_ = filteredVideoTags[filteredVideoTags.length - 1].pts; 45189 } 45190 45191 var tags = this.getOrderedTags_(filteredVideoTags, filteredAudioTags); 45192 45193 if (tags.length === 0) { 45194 return; 45195 } 45196 45197 // If we are appending data that comes before our target pts, we want to tell 45198 // the swf to adjust its notion of current time to account for the extra tags 45199 // we are appending to complete the GOP that intersects with targetPts 45200 if (tags[0].pts < videoTargetPts && tech.seeking()) { 45201 var fudgeFactor = 1 / 30; 45202 var currentTime = tech.currentTime(); 45203 var diff = (videoTargetPts - tags[0].pts) / 1e3; 45204 var adjustedTime = currentTime - diff; 45205 45206 if (adjustedTime < fudgeFactor) { 45207 adjustedTime = 0; 45208 } 45209 45210 try { 45211 this.mediaSource_.swfObj.vjs_adjustCurrentTime(adjustedTime); 45212 } catch (e) { 45213 // no-op for backwards compatability of swf. If adjustCurrentTime fails, 45214 // the swf may incorrectly report currentTime and buffered ranges 45215 // but should not affect playback over than the time displayed on the 45216 // progress bar is inaccurate 45217 } 45218 } 45219 45220 // concatenate the bytes into a single segment 45221 for (var i = 0; i < tags.length; i++) { 45222 segmentByteLength += tags[i].bytes.byteLength; 45223 } 45224 segment = new Uint8Array(segmentByteLength); 45225 for (var i = 0, j = 0; i < tags.length; i++) { 45226 segment.set(tags[i].bytes, j); 45227 j += tags[i].bytes.byteLength; 45228 } 45229 45230 return segment; 45231 } 45232 45233 /** 45234 * Assemble the FLV tags in decoder order. 45235 * 45236 * @private 45237 * @param {Array} videoTags list of video tags 45238 * @param {Array} audioTags list of audio tags 45239 */ 45240 }, { 45241 key: 'getOrderedTags_', 45242 value: function getOrderedTags_(videoTags, audioTags) { 45243 var tag = undefined; 45244 var tags = []; 45245 45246 while (videoTags.length || audioTags.length) { 45247 if (!videoTags.length) { 45248 // only audio tags remain 45249 tag = audioTags.shift(); 45250 } else if (!audioTags.length) { 45251 // only video tags remain 45252 tag = videoTags.shift(); 45253 } else if (audioTags[0].dts < videoTags[0].dts) { 45254 // audio should be decoded next 45255 tag = audioTags.shift(); 45256 } else { 45257 // video should be decoded next 45258 tag = videoTags.shift(); 45259 } 45260 45261 tags.push(tag); 45262 } 45263 45264 return tags; 45265 } 45266 }, { 45267 key: 'onHlsReset_', 45268 value: function onHlsReset_() { 45269 this.transmuxer_.postMessage({ action: 'resetCaptions' }); 45270 } 45271 }]); 45272 45273 return FlashSourceBuffer; 45274})(_videoJs2['default'].EventTarget); 45275 45276exports['default'] = FlashSourceBuffer; 45277module.exports = exports['default']; 45278}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 45279},{"./add-text-track-data":64,"./create-text-tracks-if-necessary":66,"./flash-constants":67,"./flash-transmuxer-worker":70,"./remove-cues-from-track":72,"global/window":32,"mux.js/lib/flv":46,"webworkify":76}],70:[function(require,module,exports){ 45280/** 45281 * @file flash-transmuxer-worker.js 45282 */ 45283'use strict'; 45284 45285Object.defineProperty(exports, '__esModule', { 45286 value: true 45287}); 45288 45289var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 45290 45291function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 45292 45293function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 45294 45295var _globalWindow = require('global/window'); 45296 45297var _globalWindow2 = _interopRequireDefault(_globalWindow); 45298 45299var _muxJsLibFlv = require('mux.js/lib/flv'); 45300 45301var _muxJsLibFlv2 = _interopRequireDefault(_muxJsLibFlv); 45302 45303/**
45304 * Re-emits transmuxer events by converting them into messages to the 45305 * world outside the worker. 45306 * 45307 * @param {Object} transmuxer the transmuxer to wire events on 45308 * @private 45309 */ 45310var wireTransmuxerEvents = function wireTransmuxerEvents(transmuxer) { 45311 transmuxer.on('data', function (segment) { 45312 _globalWindow2['default'].postMessage({ 45313 action: 'data', 45314 segment: segment 45315 }); 45316 }); 45317 45318 transmuxer.on('done', function (data) { 45319 _globalWindow2['default'].postMessage({ action: 'done' }); 45320 }); 45321}; 45322 45323/** 45324 * All incoming messages route through this hash. If no function exists 45325 * to handle an incoming message, then we ignore the message. 45326 * 45327 * @class MessageHandlers 45328 * @param {Object} options the options to initialize with 45329 */ 45330 45331var MessageHandlers = (function () { 45332 function MessageHandlers(options) { 45333 _classCallCheck(this, MessageHandlers); 45334 45335 this.options = options || {}; 45336 this.init(); 45337 } 45338 45339 /** 45340 * Our web wroker interface so that things can talk to mux.js 45341 * that will be running in a web worker. The scope is passed to this by 45342 * webworkify. 45343 * 45344 * @param {Object} self the scope for the web worker 45345 */ 45346 45347 /** 45348 * initialize our web worker and wire all the events. 45349 */ 45350 45351 _createClass(MessageHandlers, [{ 45352 key: 'init', 45353 value: function init() { 45354 if (this.transmuxer) { 45355 this.transmuxer.dispose(); 45356 } 45357 this.transmuxer = new _muxJsLibFlv2['default'].Transmuxer(this.options); 45358 wireTransmuxerEvents(this.transmuxer); 45359 } 45360 45361 /** 45362 * Adds data (a ts segment) to the start of the transmuxer pipeline for 45363 * processing. 45364 * 45365 * @param {ArrayBuffer} data data to push into the muxer 45366 */ 45367 }, { 45368 key: 'push', 45369 value: function push(data) { 45370 // Cast array buffer to correct type for transmuxer 45371 var segment = new Uint8Array(data.data, data.byteOffset, data.byteLength); 45372 45373 this.transmuxer.push(segment); 45374 } 45375 45376 /** 45377 * Recreate the transmuxer so that the next segment added via `push` 45378 * start with a fresh transmuxer. 45379 */ 45380 }, { 45381 key: 'reset', 45382 value: function reset() { 45383 this.init(); 45384 } 45385 45386 /** 45387 * Forces the pipeline to finish processing the last segment and emit its 45388 * results. 45389 */ 45390 }, { 45391 key: 'flush', 45392 value: function flush() { 45393 this.transmuxer.flush(); 45394 } 45395 }, { 45396 key: 'resetCaptions', 45397 value: function resetCaptions() { 45398 this.transmuxer.resetCaptions(); 45399 } 45400 }]); 45401 45402 return MessageHandlers; 45403})(); 45404 45405var FlashTransmuxerWorker = function FlashTransmuxerWorker(self) { 45406 self.onmessage = function (event) { 45407 if (event.data.action === 'init' && event.data.options) { 45408 this.messageHandlers = new MessageHandlers(event.data.options); 45409 return; 45410 } 45411 45412 if (!this.messageHandlers) { 45413 this.messageHandlers = new MessageHandlers(); 45414 } 45415 45416 if (event.data && event.data.action && event.data.action !== 'init') { 45417 if (this.messageHandlers[event.data.action]) { 45418 this.messageHandlers[event.data.action](event.data); 45419 } 45420 } 45421 }; 45422}; 45423 45424exports['default'] = function (self) { 45425 return new FlashTransmuxerWorker(self); 45426}; 45427 45428module.exports = exports['default']; 45429},{"global/window":32,"mux.js/lib/flv":46}],71:[function(require,module,exports){ 45430(function (global){ 45431/** 45432 * @file html-media-source.js 45433 */ 45434'use strict'; 45435 45436Object.defineProperty(exports, '__esModule', { 45437 value: true 45438}); 45439 45440var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 45441 45442var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 45443 45444function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 45445 45446function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 45447 45448function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
45448onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 45449 45450var _globalWindow = require('global/window'); 45451 45452var _globalWindow2 = _interopRequireDefault(_globalWindow); 45453 45454var _globalDocument = require('global/document'); 45455 45456var _globalDocument2 = _interopRequireDefault(_globalDocument); 45457 45458var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null); 45459 45460var _videoJs2 = _interopRequireDefault(_videoJs); 45461 45462var _virtualSourceBuffer = require('./virtual-source-buffer'); 45463 45464var _virtualSourceBuffer2 = _interopRequireDefault(_virtualSourceBuffer); 45465 45466var _addTextTrackData = require('./add-text-track-data'); 45467 45468var _codecUtils = require('./codec-utils'); 45469 45470/** 45471 * Our MediaSource implementation in HTML, mimics native 45472 * MediaSource where/if possible. 45473 * 45474 * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource 45475 * @class HtmlMediaSource 45476 * @extends videojs.EventTarget 45477 */ 45478 45479var HtmlMediaSource = (function (_videojs$EventTarget) { 45480 _inherits(HtmlMediaSource, _videojs$EventTarget); 45481 45482 function HtmlMediaSource() { 45483 var _this = this; 45484 45485 _classCallCheck(this, HtmlMediaSource); 45486 45487 _get(Object.getPrototypeOf(HtmlMediaSource.prototype), 'constructor', this).call(this); 45488 var property = undefined; 45489 45490 this.nativeMediaSource_ = new _globalWindow2['default'].MediaSource(); 45491 // delegate to the native MediaSource's methods by default 45492 for (property in this.nativeMediaSource_) { 45493 if (!(property in HtmlMediaSource.prototype) && typeof this.nativeMediaSource_[property] === 'function') { 45494 this[property] = this.nativeMediaSource_[property].bind(this.nativeMediaSource_); 45495 } 45496 } 45497 45498 // emulate `duration` and `seekable` until seeking can be 45499 // handled uniformly for live streams 45500 // see https://github.com/w3c/media-source/issues/5 45501 this.duration_ = NaN; 45502 Object.defineProperty(this, 'duration', { 45503 get: function get() { 45504 if (this.duration_ === Infinity) { 45505 return this.duration_; 45506 } 45507 return this.nativeMediaSource_.duration; 45508 }, 45509 set: function set(duration) { 45510 this.duration_ = duration; 45511 if (duration !== Infinity) { 45512 this.nativeMediaSource_.duration = duration; 45513 return; 45514 } 45515 } 45516 }); 45517 Object.defineProperty(this, 'seekable', { 45518 get: function get() { 45519 if (this.duration_ === Infinity) { 45520 return _videoJs2['default'].createTimeRanges([[0, this.nativeMediaSource_.duration]]); 45521 } 45522 return this.nativeMediaSource_.seekable; 45523 } 45524 }); 45525 45526 Object.defineProperty(this, 'readyState', { 45527 get: function get() { 45528 return this.nativeMediaSource_.readyState; 45529 } 45530 }); 45531 45532 Object.defineProperty(this, 'activeSourceBuffers', { 45533 get: function get() { 45534 return this.activeSourceBuffers_; 45535 } 45536 }); 45537 45538 // the list of virtual and native SourceBuffers created by this 45539 // MediaSource 45540 this.sourceBuffers = []; 45541 45542 this.activeSourceBuffers_ = []; 45543 45544 /** 45545 * update the list of active source buffers based upon various 45546 * imformation from HLS and video.js 45547 * 45548 * @private 45549 */ 45550 this.updateActiveSourceBuffers_ = function () { 45551 // Retain the reference but empty the array 45552 _this.activeSourceBuffers_.length = 0; 45553 45554 // If there is only one source buffer, then it will always be active and audio will 45555 // be disabled based on the codec of the source buffer 45556 if (_this.sourceBuffers.length === 1) { 45557 var sourceBuffer = _this.sourceBuffers[0]; 45558 45559 sourceBuffer.appendAudioInitSegment_ = true; 45560 sourceBuffer.audioDisabled_ = !sourceBuffer.audioCodec_; 45561 _this.activeSourceBuffers_.push(sourceBuffer); 45562 return; 45563 } 45564 45565 // There are 2 source buffers, a combined (possibly video only) source buffer and 45566 // and an audio only source buffer. 45567 // By default, the audio in the combined virtual source buffer is enabled 45568 // and the audio-only source buffer (if it exists) is disabled. 45569 var disableCombined = false;
45570 var disableAudioOnly = true; 45571 45572 // TODO: maybe we can store the sourcebuffers on the track objects? 45573 // safari may do something like this 45574 for (var i = 0; i < _this.player_.audioTracks().length; i++) { 45575 var track = _this.player_.audioTracks()[i]; 45576 45577 if (track.enabled && track.kind !== 'main') { 45578 // The enabled track is an alternate audio track so disable the audio in 45579 // the combined source buffer and enable the audio-only source buffer. 45580 disableCombined = true; 45581 disableAudioOnly = false; 45582 break; 45583 } 45584 } 45585 45586 _this.sourceBuffers.forEach(function (sourceBuffer) { 45587 /* eslinst-disable */ 45588 // TODO once codecs are required, we can switch to using the codecs to determine 45589 // what stream is the video stream, rather than relying on videoTracks 45590 /* eslinst-enable */ 45591 45592 sourceBuffer.appendAudioInitSegment_ = true; 45593 45594 if (sourceBuffer.videoCodec_ && sourceBuffer.audioCodec_) { 45595 // combined 45596 sourceBuffer.audioDisabled_ = disableCombined; 45597 } else if (sourceBuffer.videoCodec_ && !sourceBuffer.audioCodec_) { 45598 // If the "combined" source buffer is video only, then we do not want 45599 // disable the audio-only source buffer (this is mostly for demuxed 45600 // audio and video hls) 45601 sourceBuffer.audioDisabled_ = true; 45602 disableAudioOnly = false; 45603 } else if (!sourceBuffer.videoCodec_ && sourceBuffer.audioCodec_) { 45604 // audio only 45605 sourceBuffer.audioDisabled_ = disableAudioOnly; 45606 if (disableAudioOnly) { 45607 return; 45608 } 45609 } 45610 45611 _this.activeSourceBuffers_.push(sourceBuffer); 45612 }); 45613 }; 45614 45615 this.onPlayerMediachange_ = function () { 45616 _this.sourceBuffers.forEach(function (sourceBuffer) { 45617 sourceBuffer.appendAudioInitSegment_ = true; 45618 }); 45619 }; 45620 45621 this.onHlsReset_ = function () { 45622 _this.sourceBuffers.forEach(function (sourceBuffer) { 45623 if (sourceBuffer.transmuxer_) { 45624 sourceBuffer.transmuxer_.postMessage({ action: 'resetCaptions' }); 45625 } 45626 }); 45627 }; 45628 45629 // Re-emit MediaSource events on the polyfill 45630 ['sourceopen', 'sourceclose', 'sourceended'].forEach(function (eventName) { 45631 this.nativeMediaSource_.addEventListener(eventName, this.trigger.bind(this)); 45632 }, this); 45633 45634 // capture the associated player when the MediaSource is 45635 // successfully attached 45636 this.on('sourceopen', function (event) { 45637 // Get the player this MediaSource is attached to 45638 var video = _globalDocument2['default'].querySelector('[src="' + _this.url_ + '"]'); 45639 45640 if (!video) { 45641 return; 45642 } 45643 45644 _this.player_ = (0, _videoJs2['default'])(video.parentNode); 45645 45646 _this.player_.tech_.on('hls-reset', _this.onHlsReset_); 45647 45648 if (_this.player_.audioTracks && _this.player_.audioTracks()) { 45649 _this.player_.audioTracks().on('change', _this.updateActiveSourceBuffers_); 45650 _this.player_.audioTracks().on('addtrack', _this.updateActiveSourceBuffers_); 45651 _this.player_.audioTracks().on('removetrack', _this.updateActiveSourceBuffers_); 45652 } 45653 45654 _this.player_.on('mediachange', _this.onPlayerMediachange_); 45655 }); 45656 45657 this.on('sourceended', function (event) { 45658 var duration = (0, _addTextTrackData.durationOfVideo)(_this.duration); 45659 45660 for (var i = 0; i < _this.sourceBuffers.length; i++) { 45661 var sourcebuffer = _this.sourceBuffers[i]; 45662 var cues = sourcebuffer.metadataTrack_ && sourcebuffer.metadataTrack_.cues; 45663 45664 if (cues && cues.length) { 45665 cues[cues.length - 1].endTime = duration; 45666 } 45667 } 45668 }); 45669 45670 // explicitly terminate any WebWorkers that were created 45671 // by SourceHandlers 45672 this.on('sourceclose', function (event) { 45673 this.sourceBuffers.forEach(function (sourceBuffer) { 45674 if (sourceBuffer.transmuxer_) { 45675 sourceBuffer.transmuxer_.terminate(); 45676 } 45677 }); 45678 45679 this.sourceBuffers.length = 0; 45680 if (!this.player_) { 45681 return; 45682 } 45683 45684 if (this.player_.audioTracks && this.player_.audioTracks()) { 45685 this.player_.audioTracks().off('change', this.updateActiveSourceBuffers_); 45686 this.player_.audioTracks().off('addtrack', this.updateActiveSourceBuffers_); 45687 this.player_.audioTracks().off('removetrack', this.updateActiveSourceBuffers_); 45688 } 45689 45690 // We can only change this if the player hasn't been disposed of yet 45691 // because `off` eventually tries to use the el_ property. If it has 45692 // been disposed of, then don't worry about it because there are no 45693 // event handlers left to unbind anyway 45694 if (this.player_.el_) { 45695 this.player_.off('mediachange', this.onPlayerMediachange_); 45696 this.player_.tech_.off('hls-reset', this.onHlsReset_); 45697 } 45698 }); 45699 } 45700 45701 /** 45702 * Add a range that that can now be seeked to. 45703 * 45704 * @param {Double} start where to start the addition 45705 * @param {Double} end where to end the addition 45706 * @private 45707 */ 45708 45709 _createClass(HtmlMediaSource, [{ 45710 key: 'addSeekableRange_', 45711 value: function addSeekableRange_(start, end) { 45712 var error = undefined; 45713 45714 if (this.duration !== Infinity) { 45715 error = new Error('MediaSource.addSeekableRange() can only be invoked ' + 'when the duration is Infinity');
45716 error.name = 'InvalidStateError'; 45717 error.code = 11; 45718 throw error; 45719 } 45720 45721 if (end > this.nativeMediaSource_.duration || isNaN(this.nativeMediaSource_.duration)) { 45722 this.nativeMediaSource_.duration = end; 45723 } 45724 } 45725 45726 /** 45727 * Add a source buffer to the media source. 45728 * 45729 * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/addSourceBuffer 45730 * @param {String} type the content-type of the content 45731 * @return {Object} the created source buffer 45732 */ 45733 }, { 45734 key: 'addSourceBuffer', 45735 value: function addSourceBuffer(type) { 45736 var buffer = undefined; 45737 var parsedType = (0, _codecUtils.parseContentType)(type); 45738 45739 // Create a VirtualSourceBuffer to transmux MPEG-2 transport 45740 // stream segments into fragmented MP4s 45741 if (/^(video|audio)\/mp2t$/i.test(parsedType.type)) { 45742 var codecs = []; 45743 45744 if (parsedType.parameters && parsedType.parameters.codecs) { 45745 codecs = parsedType.parameters.codecs.split(','); 45746 codecs = (0, _codecUtils.translateLegacyCodecs)(codecs); 45747 codecs = codecs.filter(function (codec) { 45748 return (0, _codecUtils.isAudioCodec)(codec) || (0, _codecUtils.isVideoCodec)(codec); 45749 }); 45750 } 45751 45752 if (codecs.length === 0) { 45753 codecs = ['avc1.4d400d', 'mp4a.40.2']; 45754 } 45755 45756 buffer = new _virtualSourceBuffer2['default'](this, codecs); 45757 45758 if (this.sourceBuffers.length !== 0) { 45759 // If another VirtualSourceBuffer already exists, then we are creating a 45760 // SourceBuffer for an alternate audio track and therefore we know that 45761 // the source has both an audio and video track. 45762 // That means we should trigger the manual creation of the real 45763 // SourceBuffers instead of waiting for the transmuxer to return data 45764 this.sourceBuffers[0].createRealSourceBuffers_(); 45765 buffer.createRealSourceBuffers_(); 45766 45767 // Automatically disable the audio on the first source buffer if 45768 // a second source buffer is ever created 45769 this.sourceBuffers[0].audioDisabled_ = true; 45770 } 45771 } else { 45772 // delegate to the native implementation 45773 buffer = this.nativeMediaSource_.addSourceBuffer(type); 45774 } 45775 45776 this.sourceBuffers.push(buffer); 45777 return buffer; 45778 } 45779 }]); 45780 45781 return HtmlMediaSource; 45782})(_videoJs2['default'].EventTarget); 45783 45784exports['default'] = HtmlMediaSource; 45785module.exports = exports['default']; 45786}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 45787},{"./add-text-track-data":64,"./codec-utils":65,"./virtual-source-buffer":75,"global/document":31,"global/window":32}],72:[function(require,module,exports){ 45788/** 45789 * @file remove-cues-from-track.js 45790 */ 45791 45792/** 45793 * Remove cues from a track on video.js. 45794 * 45795 * @param {Double} start start of where we should remove the cue 45796 * @param {Double} end end of where the we should remove the cue 45797 * @param {Object} track the text track to remove the cues from 45798 * @private 45799 */ 45800"use strict"; 45801 45802Object.defineProperty(exports, "__esModule", { 45803 value: true 45804}); 45805var removeCuesFromTrack = function removeCuesFromTrack(start, end, track) { 45806 var i = undefined; 45807 var cue = undefined; 45808 45809 if (!track) { 45810 return; 45811 } 45812 45813 if (!track.cues) { 45814 return; 45815 } 45816 45817 i = track.cues.length; 45818 45819 while (i--) { 45820 cue = track.cues[i]; 45821 45822 // Remove any overlapping cue 45823 if (cue.startTime <= end && cue.endTime >= start) { 45824 track.removeCue(cue); 45825 } 45826 } 45827}; 45828 45829exports["default"] = removeCuesFromTrack; 45830module.exports = exports["default"]; 45831},{}],73:[function(require,module,exports){ 45832/** 45833 * @file transmuxer-worker.js 45834 */ 45835 45836/** 45837 * videojs-contrib-media-sources 45838 * 45839 * Copyright (c) 2015 Brightcove 45840 * All rights reserved. 45841 * 45842 * Handles communication between the browser-world and the mux.js 45843 * transmuxer running inside of a WebWorker by exposing a simple 45844 * message-based interface to a Transmuxer object. 45845 */ 45846'use strict'; 45847 45848Object.defineProperty(exports, '__esModule', { 45849 value: true 45850}); 45851 45852var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 45853 45854function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 45855 45856function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 45857 45858var _globalWindow = require('global/window'); 45859 45860var _globalWindow2 = _interopRequireDefault(_globalWindow); 45861 45862var _muxJsLibMp4 = require('mux.js/lib/mp4'); 45863 45864var _muxJsLibMp42 = _interopRequireDefault(_muxJsLibMp4); 45865 45866/**
45867 * Re-emits transmuxer events by converting them into messages to the 45868 * world outside the worker. 45869 * 45870 * @param {Object} transmuxer the transmuxer to wire events on 45871 * @private 45872 */ 45873var wireTransmuxerEvents = function wireTransmuxerEvents(transmuxer) { 45874 transmuxer.on('data', function (segment) { 45875 // transfer ownership of the underlying ArrayBuffer 45876 // instead of doing a copy to save memory 45877 // ArrayBuffers are transferable but generic TypedArrays are not 45878 // @link https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#Passing_data_by_transferring_ownership_(transferable_objects) 45879 var initArray = segment.initSegment; 45880 45881 segment.initSegment = { 45882 data: initArray.buffer, 45883 byteOffset: initArray.byteOffset, 45884 byteLength: initArray.byteLength 45885 }; 45886 45887 var typedArray = segment.data; 45888 45889 segment.data = typedArray.buffer; 45890 _globalWindow2['default'].postMessage({ 45891 action: 'data', 45892 segment: segment, 45893 byteOffset: typedArray.byteOffset, 45894 byteLength: typedArray.byteLength 45895 }, [segment.data]); 45896 }); 45897 45898 if (transmuxer.captionStream) { 45899 transmuxer.captionStream.on('data', function (caption) { 45900 _globalWindow2['default'].postMessage({ 45901 action: 'caption', 45902 data: caption 45903 }); 45904 }); 45905 } 45906 45907 transmuxer.on('done', function (data) { 45908 _globalWindow2['default'].postMessage({ action: 'done' }); 45909 }); 45910}; 45911 45912/** 45913 * All incoming messages route through this hash. If no function exists 45914 * to handle an incoming message, then we ignore the message. 45915 * 45916 * @class MessageHandlers 45917 * @param {Object} options the options to initialize with 45918 */ 45919 45920var MessageHandlers = (function () { 45921 function MessageHandlers(options) { 45922 _classCallCheck(this, MessageHandlers); 45923 45924 this.options = options || {}; 45925 this.init(); 45926 } 45927 45928 /** 45929 * Our web wroker interface so that things can talk to mux.js 45930 * that will be running in a web worker. the scope is passed to this by 45931 * webworkify. 45932 * 45933 * @param {Object} self the scope for the web worker 45934 */ 45935 45936 /** 45937 * initialize our web worker and wire all the events. 45938 */ 45939 45940 _createClass(MessageHandlers, [{ 45941 key: 'init', 45942 value: function init() { 45943 if (this.transmuxer) { 45944 this.transmuxer.dispose(); 45945 } 45946 this.transmuxer = new _muxJsLibMp42['default'].Transmuxer(this.options); 45947 wireTransmuxerEvents(this.transmuxer); 45948 } 45949 45950 /** 45951 * Adds data (a ts segment) to the start of the transmuxer pipeline for 45952 * processing. 45953 * 45954 * @param {ArrayBuffer} data data to push into the muxer 45955 */ 45956 }, { 45957 key: 'push', 45958 value: function push(data) { 45959 // Cast array buffer to correct type for transmuxer 45960 var segment = new Uint8Array(data.data, data.byteOffset, data.byteLength); 45961 45962 this.transmuxer.push(segment); 45963 } 45964 45965 /** 45966 * Recreate the transmuxer so that the next segment added via `push` 45967 * start with a fresh transmuxer. 45968 */ 45969 }, { 45970 key: 'reset', 45971 value: function reset() { 45972 this.init(); 45973 } 45974 45975 /** 45976 * Set the value that will be used as the `baseMediaDecodeTime` time for the 45977 * next segment pushed in. Subsequent segments will have their `baseMediaDecodeTime` 45978 * set relative to the first based on the PTS values. 45979 * 45980 * @param {Object} data used to set the timestamp offset in the muxer 45981 */ 45982 }, { 45983 key: 'setTimestampOffset', 45984 value: function setTimestampOffset(data) { 45985 var timestampOffset = data.timestampOffset || 0; 45986 45987 this.transmuxer.setBaseMediaDecodeTime(Math.round(timestampOffset * 90000)); 45988 } 45989 }, { 45990 key: 'setAudioAppendStart', 45991 value: function setAudioAppendStart(data) { 45992 this.transmuxer.setAudioAppendStart(Math.ceil(data.appendStart * 90000)); 45993 } 45994 45995 /** 45996 * Forces the pipeline to finish processing the last segment and emit it's 45997 * results. 45998 * 45999 * @param {Object} data event data, not really used 46000 */ 46001 }, { 46002 key: 'flush', 46003 value: function flush(data) { 46004 this.transmuxer.flush(); 46005 } 46006 }, { 46007 key: 'resetCaptions', 46008 value: function resetCaptions() { 46009 this.transmuxer.resetCaptions(); 46010 } 46011 }]); 46012 46013 return MessageHandlers; 46014})(); 46015 46016var TransmuxerWorker = function TransmuxerWorker(self) { 46017 self.onmessage = function (event) {
46018 if (event.data.action === 'init' && event.data.options) { 46019 this.messageHandlers = new MessageHandlers(event.data.options); 46020 return; 46021 } 46022 46023 if (!this.messageHandlers) { 46024 this.messageHandlers = new MessageHandlers(); 46025 } 46026 46027 if (event.data && event.data.action && event.data.action !== 'init') { 46028 if (this.messageHandlers[event.data.action]) { 46029 this.messageHandlers[event.data.action](event.data); 46030 } 46031 } 46032 }; 46033}; 46034 46035exports['default'] = function (self) { 46036 return new TransmuxerWorker(self); 46037}; 46038 46039module.exports = exports['default']; 46040},{"global/window":32,"mux.js/lib/mp4":55}],74:[function(require,module,exports){ 46041(function (global){ 46042/** 46043 * @file videojs-contrib-media-sources.js 46044 */ 46045'use strict'; 46046 46047Object.defineProperty(exports, '__esModule', { 46048 value: true 46049}); 46050 46051function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 46052 46053var _globalWindow = require('global/window'); 46054 46055var _globalWindow2 = _interopRequireDefault(_globalWindow); 46056 46057var _flashMediaSource = require('./flash-media-source'); 46058 46059var _flashMediaSource2 = _interopRequireDefault(_flashMediaSource); 46060 46061var _htmlMediaSource = require('./html-media-source'); 46062 46063var _htmlMediaSource2 = _interopRequireDefault(_htmlMediaSource); 46064 46065var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null); 46066 46067var _videoJs2 = _interopRequireDefault(_videoJs); 46068 46069var urlCount = 0; 46070 46071// ------------ 46072// Media Source 46073// ------------ 46074 46075var defaults = { 46076 // how to determine the MediaSource implementation to use. There 46077 // are three available modes: 46078 // - auto: use native MediaSources where available and Flash 46079 // everywhere else 46080 // - html5: always use native MediaSources 46081 // - flash: always use the Flash MediaSource polyfill 46082 mode: 'auto' 46083}; 46084 46085// store references to the media sources so they can be connected 46086// to a video element (a swf object) 46087// TODO: can we store this somewhere local to this module? 46088_videoJs2['default'].mediaSources = {}; 46089 46090/** 46091 * Provide a method for a swf object to notify JS that a 46092 * media source is now open. 46093 * 46094 * @param {String} msObjectURL string referencing the MSE Object URL 46095 * @param {String} swfId the swf id 46096 */ 46097var open = function open(msObjectURL, swfId) { 46098 var mediaSource = _videoJs2['default'].mediaSources[msObjectURL]; 46099 46100 if (mediaSource) { 46101 mediaSource.trigger({ type: 'sourceopen', swfId: swfId }); 46102 } else { 46103 throw new Error('Media Source not found (Video.js)'); 46104 } 46105}; 46106 46107/** 46108 * Check to see if the native MediaSource object exists and supports 46109 * an MP4 container with both H.264 video and AAC-LC audio. 46110 * 46111 * @return {Boolean} if native media sources are supported 46112 */ 46113var supportsNativeMediaSources = function supportsNativeMediaSources() { 46114 return !!_globalWindow2['default'].MediaSource && !!_globalWindow2['default'].MediaSource.isTypeSupported && _globalWindow2['default'].MediaSource.isTypeSupported('video/mp4;codecs="avc1.4d400d,mp4a.40.2"'); 46115}; 46116 46117/** 46118 * An emulation of the MediaSource API so that we can support 46119 * native and non-native functionality such as flash and 46120 * video/mp2t videos. returns an instance of HtmlMediaSource or 46121 * FlashMediaSource depending on what is supported and what options 46122 * are passed in. 46123 * 46124 * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/MediaSource 46125 * @param {Object} options options to use during setup. 46126 */ 46127var MediaSource = function MediaSource(options) { 46128 var settings = _videoJs2['default'].mergeOptions(defaults, options); 46129 46130 this.MediaSource = { 46131 open: open, 46132 supportsNativeMediaSources: supportsNativeMediaSources 46133 }; 46134 46135 // determine whether HTML MediaSources should be used 46136 if (settings.mode === 'html5' || settings.mode === 'auto' && supportsNativeMediaSources()) { 46137 return new _htmlMediaSource2['default'](); 46138 } else if (_videoJs2['default'].getTech('Flash')) { 46139 return new _flashMediaSource2['default'](); 46140 } 46141 46142 throw new Error('Cannot use Flash or Html5 to create a MediaSource for this video'); 46143}; 46144 46145exports.MediaSource = MediaSource; 46146MediaSource.open = open; 46147MediaSource.supportsNativeMediaSources = supportsNativeMediaSources; 46148 46149/** 46150 * A wrapper around the native URL for our MSE object 46151 * implementation, this object is exposed under videojs.URL 46152 * 46153 * @link https://developer.mozilla.org/en-US/docs/Web/API/URL/URL 46154 */ 46155var URL = { 46156 /** 46157 * A wrapper around the native createObjectURL for our objects. 46158 * This function maps a native or emulated mediaSource to a blob 46159 * url so that it can be loaded into video.js 46160 * 46161 * @link https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL 46162 * @param {MediaSource} object the object to create a blob url to 46163 */ 46164 createObjectURL: function createObjectURL(object) { 46165 var objectUrlPrefix = 'blob:vjs-media-source/'; 46166 var url = undefined; 46167 46168 // use the native MediaSource to generate an object URL 46169 if (object instanceof _htmlMediaSource2['default']) { 46170 url = _globalWindow2['default'].URL.createObjectURL(object.nativeMediaSource_); 46171 object.url_ = url; 46172 return url; 46173 } 46174 // if the object isn't an emulated MediaSource, delegate to the 46175 // native implementation 46176 if (!(object instanceof _flashMediaSource2['default'])) { 46177 url = _globalWindow2['default'].URL.createObjectURL(object); 46178 object.url_ = url; 46179 return url; 46180 } 46181 46182 // build a URL that can be used to map back to the emulated 46183 // MediaSource 46184 url = objectUrlPrefix + urlCount; 46185 46186 urlCount++; 46187 46188 // setup the mapping back to object 46189 _videoJs2['default'].mediaSources[url] = object; 46190 46191 return url; 46192 } 46193}; 46194 46195exports.URL = URL; 46196_videoJs2['default'].MediaSource = MediaSource; 46197_videoJs2['default'].URL = URL; 46198}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 46199},{"./flash-media-source":68,"./html-media-source":71,"global/window":32}],75:[function(require,module,exports){ 46200(function (global){ 46201/** 46202 * @file virtual-source-buffer.js 46203 */ 46204'use strict'; 46205 46206Object.defineProperty(exports, '__esModule', { 46207 value: true 46208}); 46209 46210var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 46211 46212var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 46213 46214function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 46215 46216function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 46217 46218function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
46218onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 46219 46220var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null); 46221 46222var _videoJs2 = _interopRequireDefault(_videoJs); 46223 46224var _createTextTracksIfNecessary = require('./create-text-tracks-if-necessary'); 46225 46226var _createTextTracksIfNecessary2 = _interopRequireDefault(_createTextTracksIfNecessary); 46227 46228var _removeCuesFromTrack = require('./remove-cues-from-track'); 46229 46230var _removeCuesFromTrack2 = _interopRequireDefault(_removeCuesFromTrack); 46231 46232var _addTextTrackData = require('./add-text-track-data'); 46233 46234var _webworkify = require('webworkify'); 46235 46236var _webworkify2 = _interopRequireDefault(_webworkify); 46237 46238var _transmuxerWorker = require('./transmuxer-worker'); 46239 46240var _transmuxerWorker2 = _interopRequireDefault(_transmuxerWorker); 46241 46242var _codecUtils = require('./codec-utils'); 46243 46244// We create a wrapper around the SourceBuffer so that we can manage the 46245// state of the `updating` property manually. We have to do this because 46246// Firefox changes `updating` to false long before triggering `updateend` 46247// events and that was causing strange problems in videojs-contrib-hls 46248var makeWrappedSourceBuffer = function makeWrappedSourceBuffer(mediaSource, mimeType) { 46249 var sourceBuffer = mediaSource.addSourceBuffer(mimeType); 46250 var wrapper = Object.create(null); 46251 46252 wrapper.updating = false; 46253 wrapper.realBuffer_ = sourceBuffer; 46254 46255 var _loop = function (key) { 46256 if (typeof sourceBuffer[key] === 'function') { 46257 wrapper[key] = function () { 46258 return sourceBuffer[key].apply(sourceBuffer, arguments); 46259 }; 46260 } else if (typeof wrapper[key] === 'undefined') { 46261 Object.defineProperty(wrapper, key, { 46262 get: function get() { 46263 return sourceBuffer[key]; 46264 }, 46265 set: function set(v) { 46266 return sourceBuffer[key] = v; 46267 } 46268 }); 46269 } 46270 }; 46271 46272 for (var key in sourceBuffer) { 46273 _loop(key); 46274 } 46275 46276 return wrapper; 46277}; 46278 46279/** 46280 * VirtualSourceBuffers exist so that we can transmux non native formats 46281 * into a native format, but keep the same api as a native source buffer. 46282 * It creates a transmuxer, that works in its own thread (a web worker) and 46283 * that transmuxer muxes the data into a native format. VirtualSourceBuffer will 46284 * then send all of that data to the naive sourcebuffer so that it is 46285 * indestinguishable from a natively supported format. 46286 * 46287 * @param {HtmlMediaSource} mediaSource the parent mediaSource 46288 * @param {Array} codecs array of codecs that we will be dealing with 46289 * @class VirtualSourceBuffer 46290 * @extends video.js.EventTarget 46291 */ 46292 46293var VirtualSourceBuffer = (function (_videojs$EventTarget) { 46294 _inherits(VirtualSourceBuffer, _videojs$EventTarget); 46295 46296 function VirtualSourceBuffer(mediaSource, codecs) { 46297 var _this = this; 46298 46299 _classCallCheck(this, VirtualSourceBuffer); 46300 46301 _get(Object.getPrototypeOf(VirtualSourceBuffer.prototype), 'constructor', this).call(this, _videoJs2['default'].EventTarget); 46302 this.timestampOffset_ = 0; 46303 this.pendingBuffers_ = []; 46304 this.bufferUpdating_ = false; 46305 46306 this.mediaSource_ = mediaSource; 46307 this.codecs_ = codecs; 46308 this.audioCodec_ = null; 46309 this.videoCodec_ = null; 46310 this.audioDisabled_ = false; 46311 this.appendAudioInitSegment_ = true; 46312 46313 var options = { 46314 remux: false 46315 }; 46316 46317 this.codecs_.forEach(function (codec) { 46318 if ((0, _codecUtils.isAudioCodec)(codec)) { 46319 _this.audioCodec_ = codec; 46320 } else if ((0, _codecUtils.isVideoCodec)(codec)) { 46321 _this.videoCodec_ = codec; 46322 } 46323 }); 46324 46325 // append muxed segments to their respective native buffers as 46326 // soon as they are available 46327 this.transmuxer_ = (0, _webworkify2['default'])(_transmuxerWorker2['default']); 46328 this.transmuxer_.postMessage({ action: 'init', options: options }); 46329 46330 this.transmuxer_.onmessage = function (event) { 46331 if (event.data.action === 'data') { 46332 return _this.data_(event); 46333 } 46334 46335 if (event.data.action === 'done') { 46336 return _this.done_(event); 46337 } 46338 }; 46339 46340 // this timestampOffset is a property with the side-effect of resetting 46341 // baseMediaDecodeTime in the transmuxer on the setter
46342 Object.defineProperty(this, 'timestampOffset', { 46343 get: function get() { 46344 return this.timestampOffset_; 46345 }, 46346 set: function set(val) { 46347 if (typeof val === 'number' && val >= 0) { 46348 this.timestampOffset_ = val; 46349 this.appendAudioInitSegment_ = true; 46350 46351 // We have to tell the transmuxer to set the baseMediaDecodeTime to 46352 // the desired timestampOffset for the next segment 46353 this.transmuxer_.postMessage({ 46354 action: 'setTimestampOffset', 46355 timestampOffset: val 46356 }); 46357 } 46358 } 46359 }); 46360 46361 // setting the append window affects both source buffers 46362 Object.defineProperty(this, 'appendWindowStart', { 46363 get: function get() { 46364 return (this.videoBuffer_ || this.audioBuffer_).appendWindowStart; 46365 }, 46366 set: function set(start) { 46367 if (this.videoBuffer_) { 46368 this.videoBuffer_.appendWindowStart = start; 46369 } 46370 if (this.audioBuffer_) { 46371 this.audioBuffer_.appendWindowStart = start; 46372 } 46373 } 46374 }); 46375 46376 // this buffer is "updating" if either of its native buffers are 46377 Object.defineProperty(this, 'updating', { 46378 get: function get() { 46379 return !!(this.bufferUpdating_ || !this.audioDisabled_ && this.audioBuffer_ && this.audioBuffer_.updating || this.videoBuffer_ && this.videoBuffer_.updating); 46380 } 46381 }); 46382 46383 // the buffered property is the intersection of the buffered 46384 // ranges of the native source buffers 46385 Object.defineProperty(this, 'buffered', { 46386 get: function get() { 46387 var start = null; 46388 var end = null; 46389 var arity = 0; 46390 var extents = []; 46391 var ranges = []; 46392 46393 // neither buffer has been created yet 46394 if (!this.videoBuffer_ && !this.audioBuffer_) { 46395 return _videoJs2['default'].createTimeRange(); 46396 } 46397 46398 // only one buffer is configured 46399 if (!this.videoBuffer_) { 46400 return this.audioBuffer_.buffered; 46401 } 46402 if (!this.audioBuffer_) { 46403 return this.videoBuffer_.buffered; 46404 } 46405 46406 // both buffers are configured 46407 if (this.audioDisabled_) { 46408 return this.videoBuffer_.buffered; 46409 } 46410 46411 // both buffers are empty 46412 if (this.videoBuffer_.buffered.length === 0 && this.audioBuffer_.buffered.length === 0) { 46413 return _videoJs2['default'].createTimeRange(); 46414 } 46415 46416 // Handle the case where we have both buffers and create an 46417 // intersection of the two 46418 var videoBuffered = this.videoBuffer_.buffered; 46419 var audioBuffered = this.audioBuffer_.buffered; 46420 var count = videoBuffered.length; 46421 46422 // A) Gather up all start and end times 46423 while (count--) { 46424 extents.push({ time: videoBuffered.start(count), type: 'start' }); 46425 extents.push({ time: videoBuffered.end(count), type: 'end' }); 46426 } 46427 count = audioBuffered.length; 46428 while (count--) { 46429 extents.push({ time: audioBuffered.start(count), type: 'start' }); 46430 extents.push({ time: audioBuffered.end(count), type: 'end' }); 46431 } 46432 // B) Sort them by time 46433 extents.sort(function (a, b) { 46434 return a.time - b.time; 46435 }); 46436 46437 // C) Go along one by one incrementing arity for start and decrementing 46438 // arity for ends 46439 for (count = 0; count < extents.length; count++) { 46440 if (extents[count].type === 'start') { 46441 arity++; 46442 46443 // D) If arity is ever incremented to 2 we are entering an 46444 // overlapping range 46445 if (arity === 2) { 46446 start = extents[count].time; 46447 } 46448 } else if (extents[count].type === 'end') { 46449 arity--; 46450 46451 // E) If arity is ever decremented to 1 we leaving an 46452 // overlapping range 46453 if (arity === 1) { 46454 end = extents[count].time; 46455 } 46456 } 46457 46458 // F) Record overlapping ranges 46459 if (start !== null && end !== null) { 46460 ranges.push([start, end]); 46461 start = null; 46462 end = null; 46463 } 46464 } 46465 46466 return _videoJs2['default'].createTimeRanges(ranges); 46467 } 46468 }); 46469 } 46470 46471 /** 46472 * When we get a data event from the transmuxer 46473 * we call this function and handle the data that 46474 * was sent to us 46475 * 46476 * @private 46477 * @param {Event} event the data event from the transmuxer 46478 */ 46479 46480 _createClass(VirtualSourceBuffer, [{ 46481 key: 'data_', 46482 value: function data_(event) { 46483 var segment = event.data.segment; 46484 46485 // Cast ArrayBuffer to TypedArray 46486 segment.data = new Uint8Array(segment.data, event.data.byteOffset, event.data.byteLength); 46487 46488 segment.initSegment = new Uint8Array(segment.initSegment.data, segment.initSegment.byteOffset, segment.initSegment.byteLength); 46489 46490 (0, _createTextTracksIfNecessary2['default'])(this, this.mediaSource_, segment); 46491 46492 // Add the segments to the pendingBuffers array 46493 this.pendingBuffers_.push(segment); 46494 return; 46495 } 46496 46497 /** 46498 * When we get a done event from the transmuxer 46499 * we call this function and we process all 46500 * of the pending data that we have been saving in the 46501 * data_ function 46502 * 46503 * @private 46504 * @param {Event} event the done event from the transmuxer 46505 */ 46506 }, { 46507 key: 'done_', 46508 value: function done_(event) { 46509 // Don't process and append data if the mediaSource is closed 46510 if (this.mediaSource_.readyState === 'closed') { 46511 this.pendingBuffers_.length = 0; 46512 return; 46513 } 46514 46515 // All buffers should have been flushed from the muxer 46516 // start processing anything we have received 46517 this.processPendingSegments_(); 46518 return; 46519 } 46520 46521 /** 46522 * Create our internal native audio/video source buffers and add 46523 * event handlers to them with the following conditions: 46524 * 1. they do not already exist on the mediaSource 46525 * 2. this VSB has a codec for them 46526 * 46527 * @private 46528 */
vendor: 3,306 bytes, lines 46528-46617
46528 46529 }, { 46530 key: 'createRealSourceBuffers_', 46531 value: function createRealSourceBuffers_() { 46532 var _this2 = this; 46533 46534 var types = ['audio', 'video']; 46535 46536 types.forEach(function (type) { 46537 // Don't create a SourceBuffer of this type if we don't have a 46538 // codec for it 46539 if (!_this2[type + 'Codec_']) { 46540 return; 46541 } 46542 46543 // Do nothing if a SourceBuffer of this type already exists 46544 if (_this2[type + 'Buffer_']) { 46545 return; 46546 } 46547 46548 var buffer = null; 46549 46550 // If the mediasource already has a SourceBuffer for the codec 46551 // use that 46552 if (_this2.mediaSource_[type + 'Buffer_']) { 46553 buffer = _this2.mediaSource_[type + 'Buffer_']; 46554 // In multiple audio track cases, the audio source buffer is disabled 46555 // on the main VirtualSourceBuffer by the HTMLMediaSource much earlier 46556 // than createRealSourceBuffers_ is called to create the second 46557 // VirtualSourceBuffer because that happens as a side-effect of 46558 // videojs-contrib-hls starting the audioSegmentLoader. As a result, 46559 // the audioBuffer is essentially "ownerless" and no one will toggle 46560 // the `updating` state back to false once the `updateend` event is received 46561 // 46562 // Setting `updating` to false manually will work around this 46563 // situation and allow work to continue 46564 buffer.updating = false; 46565 } else { 46566 var codecProperty = type + 'Codec_'; 46567 var mimeType = type + '/mp4;codecs="' + _this2[codecProperty] + '"'; 46568 46569 buffer = makeWrappedSourceBuffer(_this2.mediaSource_.nativeMediaSource_, mimeType); 46570 46571 _this2.mediaSource_[type + 'Buffer_'] = buffer; 46572 } 46573 46574 _this2[type + 'Buffer_'] = buffer; 46575 46576 // Wire up the events to the SourceBuffer 46577 ['update', 'updatestart', 'updateend'].forEach(function (event) { 46578 buffer.addEventListener(event, function () { 46579 // if audio is disabled 46580 if (type === 'audio' && _this2.audioDisabled_) { 46581 return; 46582 } 46583 46584 if (event === 'updateend') { 46585 _this2[type + 'Buffer_'].updating = false; 46586 } 46587 46588 var shouldTrigger = types.every(function (t) { 46589 // skip checking audio's updating status if audio 46590 // is not enabled 46591 if (t === 'audio' && _this2.audioDisabled_) { 46592 return true; 46593 } 46594 // if the other type if updating we don't trigger 46595 if (type !== t && _this2[t + 'Buffer_'] && _this2[t + 'Buffer_'].updating) { 46596 return false; 46597 } 46598 return true; 46599 }); 46600 46601 if (shouldTrigger) { 46602 return _this2.trigger(event); 46603 } 46604 }); 46605 }); 46606 }); 46607 } 46608 46609 /** 46610 * Emulate the native mediasource function, but our function will 46611 * send all of the proposed segments to the transmuxer so that we 46612 * can transmux them before we append them to our internal 46613 * native source buffers in the correct format. 46614 * 46615 * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/appendBuffer 46616 * @param {Uint8Array} segment the segment to append to the buffer 46617 */
vendor: 4,661 bytes, lines 46618-46758
46618 }, { 46619 key: 'appendBuffer', 46620 value: function appendBuffer(segment) { 46621 // Start the internal "updating" state 46622 this.bufferUpdating_ = true; 46623 46624 if (this.audioBuffer_ && this.audioBuffer_.buffered.length) { 46625 var audioBuffered = this.audioBuffer_.buffered; 46626 46627 this.transmuxer_.postMessage({ 46628 action: 'setAudioAppendStart', 46629 appendStart: audioBuffered.end(audioBuffered.length - 1) 46630 }); 46631 } 46632 46633 this.transmuxer_.postMessage({ 46634 action: 'push', 46635 // Send the typed-array of data as an ArrayBuffer so that 46636 // it can be sent as a "Transferable" and avoid the costly 46637 // memory copy 46638 data: segment.buffer, 46639 46640 // To recreate the original typed-array, we need information 46641 // about what portion of the ArrayBuffer it was a view into 46642 byteOffset: segment.byteOffset, 46643 byteLength: segment.byteLength 46644 }, [segment.buffer]); 46645 this.transmuxer_.postMessage({ action: 'flush' }); 46646 } 46647 46648 /** 46649 * Emulate the native mediasource function and remove parts 46650 * of the buffer from any of our internal buffers that exist 46651 * 46652 * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/remove 46653 * @param {Double} start position to start the remove at 46654 * @param {Double} end position to end the remove at 46655 */ 46656 }, { 46657 key: 'remove', 46658 value: function remove(start, end) { 46659 if (this.videoBuffer_) { 46660 this.videoBuffer_.updating = true; 46661 this.videoBuffer_.remove(start, end); 46662 } 46663 if (!this.audioDisabled_ && this.audioBuffer_) { 46664 this.audioBuffer_.updating = true; 46665 this.audioBuffer_.remove(start, end); 46666 } 46667 46668 // Remove Metadata Cues (id3) 46669 (0, _removeCuesFromTrack2['default'])(start, end, this.metadataTrack_); 46670 46671 // Remove Any Captions 46672 if (this.inbandTextTracks_) { 46673 for (var track in this.inbandTextTracks_) { 46674 (0, _removeCuesFromTrack2['default'])(start, end, this.inbandTextTracks_[track]); 46675 } 46676 } 46677 } 46678 46679 /** 46680 * Process any segments that the muxer has output 46681 * Concatenate segments together based on type and append them into 46682 * their respective sourceBuffers 46683 * 46684 * @private 46685 */ 46686 }, { 46687 key: 'processPendingSegments_', 46688 value: function processPendingSegments_() { 46689 var sortedSegments = { 46690 video: { 46691 segments: [], 46692 bytes: 0 46693 }, 46694 audio: { 46695 segments: [], 46696 bytes: 0 46697 }, 46698 captions: [], 46699 metadata: [] 46700 }; 46701 46702 // Sort segments into separate video/audio arrays and 46703 // keep track of their total byte lengths 46704 sortedSegments = this.pendingBuffers_.reduce(function (segmentObj, segment) { 46705 var type = segment.type; 46706 var data = segment.data; 46707 var initSegment = segment.initSegment; 46708 46709 segmentObj[type].segments.push(data); 46710 segmentObj[type].bytes += data.byteLength; 46711 46712 segmentObj[type].initSegment = initSegment; 46713 46714 // Gather any captions into a single array 46715 if (segment.captions) { 46716 segmentObj.captions = segmentObj.captions.concat(segment.captions); 46717 } 46718 46719 if (segment.info) { 46720 segmentObj[type].info = segment.info; 46721 } 46722 46723 // Gather any metadata into a single array 46724 if (segment.metadata) { 46725 segmentObj.metadata = segmentObj.metadata.concat(segment.metadata); 46726 } 46727 46728 return segmentObj; 46729 }, sortedSegments); 46730 46731 // Create the real source buffers if they don't exist by now since we 46732 // finally are sure what tracks are contained in the source 46733 if (!this.videoBuffer_ && !this.audioBuffer_) { 46734 // Remove any codecs that may have been specified by default but 46735 // are no longer applicable now 46736 if (sortedSegments.video.bytes === 0) { 46737 this.videoCodec_ = null; 46738 } 46739 if (sortedSegments.audio.bytes === 0) { 46740 this.audioCodec_ = null; 46741 } 46742 46743 this.createRealSourceBuffers_(); 46744 } 46745 46746 if (sortedSegments.audio.info) { 46747 this.mediaSource_.trigger({ type: 'audioinfo', info: sortedSegments.audio.info }); 46748 } 46749 if (sortedSegments.video.info) { 46750 this.mediaSource_.trigger({ type: 'videoinfo', info: sortedSegments.video.info }); 46751 } 46752 46753 if (this.appendAudioInitSegment_) { 46754 if (!this.audioDisabled_ && this.audioBuffer_) { 46755 sortedSegments.audio.segments.unshift(sortedSegments.audio.initSegment); 46756 sortedSegments.audio.bytes += sortedSegments.audio.initSegment.byteLength; 46757 } 46758 this.appendAudioInitSegment_ = false;
46759 } 46760 46761 // Merge multiple video and audio segments into one and append 46762 if (this.videoBuffer_) { 46763 sortedSegments.video.segments.unshift(sortedSegments.video.initSegment); 46764 sortedSegments.video.bytes += sortedSegments.video.initSegment.byteLength; 46765 this.concatAndAppendSegments_(sortedSegments.video, this.videoBuffer_); 46766 // TODO: are video tracks the only ones with text tracks? 46767 (0, _addTextTrackData.addTextTrackData)(this, sortedSegments.captions, sortedSegments.metadata); 46768 } 46769 46770 if (!this.audioDisabled_ && this.audioBuffer_) { 46771 this.concatAndAppendSegments_(sortedSegments.audio, this.audioBuffer_); 46772 } 46773 46774 this.pendingBuffers_.length = 0; 46775 46776 // We are no longer in the internal "updating" state 46777 this.bufferUpdating_ = false; 46778 } 46779 46780 /** 46781 * Combine all segments into a single Uint8Array and then append them 46782 * to the destination buffer 46783 * 46784 * @param {Object} segmentObj 46785 * @param {SourceBuffer} destinationBuffer native source buffer to append data to 46786 * @private 46787 */ 46788 }, { 46789 key: 'concatAndAppendSegments_', 46790 value: function concatAndAppendSegments_(segmentObj, destinationBuffer) { 46791 var offset = 0; 46792 var tempBuffer = undefined; 46793 46794 if (segmentObj.bytes) { 46795 tempBuffer = new Uint8Array(segmentObj.bytes); 46796 46797 // Combine the individual segments into one large typed-array 46798 segmentObj.segments.forEach(function (segment) { 46799 tempBuffer.set(segment, offset); 46800 offset += segment.byteLength; 46801 }); 46802 46803 try { 46804 destinationBuffer.updating = true; 46805 destinationBuffer.appendBuffer(tempBuffer); 46806 } catch (error) { 46807 if (this.mediaSource_.player_) { 46808 this.mediaSource_.player_.error({ 46809 code: -3, 46810 type: 'APPEND_BUFFER_ERR', 46811 message: error.message, 46812 originalError: error 46813 }); 46814 } 46815 } 46816 } 46817 } 46818 46819 /** 46820 * Emulate the native mediasource function. abort any soureBuffer 46821 * actions and throw out any un-appended data. 46822 * 46823 * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/abort 46824 */ 46825 }, { 46826 key: 'abort', 46827 value: function abort() { 46828 if (this.videoBuffer_) { 46829 this.videoBuffer_.abort(); 46830 } 46831 if (!this.audioDisabled_ && this.audioBuffer_) { 46832 this.audioBuffer_.abort(); 46833 } 46834 if (this.transmuxer_) { 46835 this.transmuxer_.postMessage({ action: 'reset' }); 46836 } 46837 this.pendingBuffers_.length = 0; 46838 this.bufferUpdating_ = false; 46839 } 46840 }]); 46841 46842 return VirtualSourceBuffer; 46843})(_videoJs2['default'].EventTarget); 46844 46845exports['default'] = VirtualSourceBuffer; 46846module.exports = exports['default']; 46847}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 46848},{"./add-text-track-data":64,"./codec-utils":65,"./create-text-tracks-if-necessary":66,"./remove-cues-from-track":72,"./transmuxer-worker":73,"webworkify":76}],76:[function(require,module,exports){ 46849var bundleFn = arguments[3]; 46850var sources = arguments[4]; 46851var cache = arguments[5]; 46852 46853var stringify = JSON.stringify; 46854 46855module.exports = function (fn) { 46856 var keys = []; 46857 var wkey; 46858 var cacheKeys = Object.keys(cache); 46859 46860 for (var i = 0, l = cacheKeys.length; i < l; i++) { 46861 var key = cacheKeys[i]; 46862 if (cache[key].exports === fn) { 46863 wkey = key; 46864 break; 46865 } 46866 } 46867 46868 if (!wkey) { 46869 wkey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16); 46870 var wcache = {}; 46871 for (var i = 0, l = cacheKeys.length; i < l; i++) { 46872 var key = cacheKeys[i]; 46873 wcache[key] = key; 46874 } 46875 sources[wkey] = [ 46876 Function(['require','module','exports'], '(' + fn + ')(self)'), 46877 wcache 46878 ]; 46879 } 46880 var skey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16); 46881 46882 var scache = {}; scache[wkey] = wkey; 46883 sources[skey] = [ 46884 Function(['require'],'require(' + stringify(wkey) + ')(self)'), 46885 scache 46886 ]; 46887 46888 var src = '(' + bundleFn + ')({' 46889 + Object.keys(sources).map(function (key) { 46890 return stringify(key) + ':[' 46891 + sources[key][0] 46892 + ',' + stringify(sources[key][1]) + ']' 46893 ; 46894 }).join(',') 46895 + '},{},[' + stringify(skey) + '])' 46896 ; 46897 46898 var URL = window.URL || window.webkitURL || window.mozURL || window.msURL; 46899 46900 return new Worker(URL.createObjectURL( 46901 new Blob([src], { type: 'text/javascript' }) 46902 )); 46903}; 46904 46905},{}],77:[function(require,module,exports){ 46906(function (global){ 46907/** 46908 * @file videojs-contrib-hls.js 46909 * 46910 * The main file for the HLS project. 46911 * License: https://github.com/videojs/videojs-contrib-hls/blob/master/LICENSE 46912 */ 46913'use strict'; 46914 46915var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 46916 46917var _get = function get(_x4, _x5, _x6) { var _again = true; _function: while (_again) { var object = _x4, property = _x5, receiver = _x6; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x4 = parent; _x5 = property; _x6 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 46918 46919function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 46920 46921function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 46922 46923function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
46923onstructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 46924 46925var _globalDocument = require('global/document'); 46926 46927var _globalDocument2 = _interopRequireDefault(_globalDocument); 46928 46929var _playlistLoader = require('./playlist-loader'); 46930 46931var _playlistLoader2 = _interopRequireDefault(_playlistLoader); 46932 46933var _playlist = require('./playlist'); 46934 46935var _playlist2 = _interopRequireDefault(_playlist); 46936 46937var _xhr = require('./xhr'); 46938 46939var _xhr2 = _interopRequireDefault(_xhr); 46940 46941var _aesDecrypter = require('aes-decrypter'); 46942 46943var _binUtils = require('./bin-utils'); 46944 46945var _binUtils2 = _interopRequireDefault(_binUtils); 46946 46947var _videojsContribMediaSources = require('videojs-contrib-media-sources'); 46948 46949var _m3u8Parser = require('m3u8-parser'); 46950 46951var _m3u8Parser2 = _interopRequireDefault(_m3u8Parser); 46952 46953var _videoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null); 46954 46955var _videoJs2 = _interopRequireDefault(_videoJs); 46956 46957var _masterPlaylistController = require('./master-playlist-controller'); 46958 46959var _config = require('./config'); 46960 46961var _config2 = _interopRequireDefault(_config); 46962 46963var _renditionMixin = require('./rendition-mixin'); 46964 46965var _renditionMixin2 = _interopRequireDefault(_renditionMixin); 46966 46967var _globalWindow = require('global/window'); 46968 46969var _globalWindow2 = _interopRequireDefault(_globalWindow); 46970 46971var _playbackWatcher = require('./playback-watcher'); 46972 46973var _playbackWatcher2 = _interopRequireDefault(_playbackWatcher); 46974 46975var _reloadSourceOnError = require('./reload-source-on-error'); 46976 46977var _reloadSourceOnError2 = _interopRequireDefault(_reloadSourceOnError); 46978 46979var _playlistSelectorsJs = require('./playlist-selectors.js'); 46980 46981var Hls = { 46982 PlaylistLoader: _playlistLoader2['default'], 46983 Playlist: _playlist2['default'], 46984 Decrypter: _aesDecrypter.Decrypter, 46985 AsyncStream: _aesDecrypter.AsyncStream, 46986 decrypt: _aesDecrypter.decrypt, 46987 utils: _binUtils2['default'], 46988 46989 STANDARD_PLAYLIST_SELECTOR: _playlistSelectorsJs.lastBandwidthSelector, 46990 INITIAL_PLAYLIST_SELECTOR: _playlistSelectorsJs.lowestBitrateCompatibleVariantSelector, 46991 comparePlaylistBandwidth: _playlistSelectorsJs.comparePlaylistBandwidth, 46992 comparePlaylistResolution: _playlistSelectorsJs.comparePlaylistResolution, 46993 46994 xhr: (0, _xhr2['default'])() 46995}; 46996 46997// 0.5 MB/s 46998var INITIAL_BANDWIDTH = 4194304; 46999 47000// Define getter/setters for config properites 47001['GOAL_BUFFER_LENGTH', 'MAX_GOAL_BUFFER_LENGTH', 'GOAL_BUFFER_LENGTH_RATE', 'BUFFER_LOW_WATER_LINE', 'MAX_BUFFER_LOW_WATER_LINE', 'BUFFER_LOW_WATER_LINE_RATE', 'BANDWIDTH_VARIANCE'].forEach(function (prop) { 47002 Object.defineProperty(Hls, prop, { 47003 get: function get() { 47004 _videoJs2['default'].log.warn('using Hls.' + prop + ' is UNSAFE be sure you know what you are doing'); 47005 return _config2['default'][prop]; 47006 }, 47007 set: function set(value) { 47008 _videoJs2['default'].log.warn('using Hls.' + prop + ' is UNSAFE be sure you know what you are doing'); 47009 47010 if (typeof value !== 'number' || value < 0) { 47011 _videoJs2['default'].log.warn('value of Hls.' + prop + ' must be greater than or equal to 0'); 47012 return; 47013 } 47014 47015 _config2['default'][prop] = value; 47016 } 47017 }); 47018}); 47019 47020/** 47021 * Updates the selectedIndex of the QualityLevelList when a mediachange happens in hls. 47022 * 47023 * @param {QualityLevelList} qualityLevels The QualityLevelList to update. 47024 * @param {PlaylistLoader} playlistLoader PlaylistLoader containing the new media info. 47025 * @function handleHlsMediaChange 47026 */ 47027var handleHlsMediaChange = function handleHlsMediaChange(qualityLevels, playlistLoader) { 47028 var newPlaylist = playlistLoader.media(); 47029 var selectedIndex = -1; 47030 47031 for (var i = 0; i < qualityLevels.length; i++) { 47032 if (qualityLevels[i].id === newPlaylist.uri) { 47033 selectedIndex = i; 47034 break; 47035 } 47036 } 47037 47038 qualityLevels.selectedIndex_ = selectedIndex; 47039 qualityLevels.trigger({ 47040 selectedIndex: selectedIndex, 47041 type: 'change' 47042 }); 47043}; 47044 47045/** 47046 * Adds quality levels to list once playlist metadata is available 47047 * 47048 * @param {QualityLevelList} qualityLevels The QualityLevelList to attach events to. 47049 * @param {Object} hls Hls object to listen to for media events. 47050 * @function handleHlsLoadedMetadata 47051 */ 47052var handleHlsLoadedMetadata = function handleHlsLoadedMetadata(qualityLevels, hls) {
47053 hls.representations().forEach(function (rep) { 47054 qualityLevels.addQualityLevel(rep); 47055 }); 47056 handleHlsMediaChange(qualityLevels, hls.playlists); 47057}; 47058 47059// HLS is a source handler, not a tech. Make sure attempts to use it 47060// as one do not cause exceptions. 47061Hls.canPlaySource = function () { 47062 return _videoJs2['default'].log.warn('HLS is no longer a tech. Please remove it from ' + 'your player\'s techOrder.'); 47063}; 47064 47065/** 47066 * Whether the browser has built-in HLS support. 47067 */ 47068Hls.supportsNativeHls = (function () { 47069 var video = _globalDocument2['default'].createElement('video'); 47070 47071 // native HLS is definitely not supported if HTML5 video isn't 47072 if (!_videoJs2['default'].getTech('Html5').isSupported()) { 47073 return false; 47074 } 47075 47076 // HLS manifests can go by many mime-types 47077 var canPlay = [ 47078 // Apple santioned 47079 'application/vnd.apple.mpegurl', 47080 // Apple sanctioned for backwards compatibility 47081 'audio/mpegurl', 47082 // Very common 47083 'audio/x-mpegurl', 47084 // Very common 47085 'application/x-mpegurl', 47086 // Included for completeness 47087 'video/x-mpegurl', 'video/mpegurl', 'application/mpegurl']; 47088 47089 return canPlay.some(function (canItPlay) { 47090 return (/maybe|probably/i.test(video.canPlayType(canItPlay)) 47091 ); 47092 }); 47093})(); 47094 47095/** 47096 * HLS is a source handler, not a tech. Make sure attempts to use it 47097 * as one do not cause exceptions. 47098 */ 47099Hls.isSupported = function () { 47100 return _videoJs2['default'].log.warn('HLS is no longer a tech. Please remove it from ' + 'your player\'s techOrder.'); 47101}; 47102 47103var Component = _videoJs2['default'].getComponent('Component'); 47104 47105/** 47106 * The Hls Handler object, where we orchestrate all of the parts 47107 * of HLS to interact with video.js 47108 * 47109 * @class HlsHandler 47110 * @extends videojs.Component 47111 * @param {Object} source the soruce object 47112 * @param {Tech} tech the parent tech object 47113 * @param {Object} options optional and required options 47114 */ 47115 47116var HlsHandler = (function (_Component) { 47117 _inherits(HlsHandler, _Component); 47118 47119 function HlsHandler(source, tech, options) { 47120 var _this = this; 47121 47122 _classCallCheck(this, HlsHandler); 47123 47124 _get(Object.getPrototypeOf(HlsHandler.prototype), 'constructor', this).call(this, tech, options.hls); 47125 47126 // tech.player() is deprecated but setup a reference to HLS for 47127 // backwards-compatibility 47128 if (tech.options_ && tech.options_.playerId) { 47129 var _player = (0, _videoJs2['default'])(tech.options_.playerId); 47130 47131 if (!_player.hasOwnProperty('hls')) { 47132 Object.defineProperty(_player, 'hls', { 47133 get: function get() { 47134 _videoJs2['default'].log.warn('player.hls is deprecated. Use player.tech_.hls instead.'); 47135 tech.trigger({ type: 'usage', name: 'hls-player-access' }); 47136 return _this; 47137 } 47138 }); 47139 } 47140 } 47141 47142 this.tech_ = tech; 47143 this.source_ = source; 47144 this.stats = {}; 47145 this.ignoreNextSeekingEvent_ = false; 47146 this.setOptions_(); 47147 47148 // overriding native HLS only works if audio tracks have been emulated 47149 // error early if we're misconfigured: 47150 if (this.options_.overrideNative && (tech.featuresNativeVideoTracks || tech.featuresNativeAudioTracks)) { 47151 throw new Error('Overriding native HLS requires emulated tracks. ' + 'See https://git.io/vMpjB'); 47152 } 47153 47154 // listen for fullscreenchange events for this player so that we 47155 // can adjust our quality selection quickly 47156 this.on(_globalDocument2['default'], ['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'MSFullscreenChange'], function (event) { 47157 var fullscreenElement = _globalDocument2['default'].fullscreenElement || _globalDocument2['default'].webkitFullscreenElement || _globalDocument2['default'].mozFullScreenElement || _globalDocument2['default'].msFullscreenElement; 47158 47159 if (fullscreenElement && fullscreenElement.contains(_this.tech_.el())) { 47160 _this.masterPlaylistController_.fastQualityChange_(); 47161 } 47162 }); 47163 47164 this.on(this.tech_, 'seeking', function () { 47165 if (this.ignoreNextSeekingEvent_) { 47166 this.ignoreNextSeekingEvent_ = false; 47167 return; 47168 } 47169 47170 this.setCurrentTime(this.tech_.currentTime()); 47171 }); 47172 this.on(this.tech_, 'error', function () { 47173 if (this.masterPlaylistController_) { 47174 this.masterPlaylistController_.pauseLoading(); 47175 } 47176 }); 47177 47178 this.on(this.tech_, 'play', this.play); 47179 } 47180 47181 /** 47182 * The Source Handler object, which informs video.js what additional 47183 * MIME types are supported and sets up playback. It is registered 47184 * automatically to the appropriate tech based on the capabilities of 47185 * the browser it is running in. It is not necessary to use or modify 47186 * this object in normal usage. 47187 */ 47188 47189 _createClass(HlsHandler, [{ 47190 key: 'setOptions_', 47191 value: function setOptions_() { 47192 var _this2 = this; 47193 47194 // defaults 47195 this.options_.withCredentials = this.options_.withCredentials || false; 47196 47197 if (typeof this.options_.blacklistDuration !== 'number') { 47198 this.options_.blacklistDuration = 5 * 60; 47199 } 47200 47201 // start playlist selection at a reasonable bandwidth for 47202 // broadband internet (0.5 MB/s) or mobile (0.0625 MB/s) 47203 if (typeof this.options_.bandwidth !== 'number') { 47204 this.options_.bandwidth = INITIAL_BANDWIDTH; 47205 } 47206 47207 // If the bandwidth number is unchanged from the initial setting 47208 // then this takes precedence over the enableLowInitialPlaylist option 47209 this.options_.enableLowInitialPlaylist = this.options_.enableLowInitialPlaylist && this.options_.bandwidth === INITIAL_BANDWIDTH; 47210 47211 // grab options passed to player.src 47212 ['withCredentials', 'bandwidth'].forEach(function (option) { 47213 if (typeof _this2.source_[option] !== 'undefined') { 47214 _this2.options_[option] = _this2.source_[option]; 47215 } 47216 }); 47217 47218 this.bandwidth = this.options_.bandwidth; 47219 } 47220 47221 /** 47222 * called when player.src gets called, handle a new source 47223 * 47224 * @param {Object} src the source object to handle 47225 */ 47226 }, { 47227 key: 'src', 47228 value: function src(_src) { 47229 var _this3 = this; 47230 47231 // do nothing if the src is falsey 47232 if (!_src) { 47233 return; 47234 } 47235 this.setOptions_(); 47236 // add master playlist controller options 47237 this.options_.url = this.source_.src; 47238 this.options_.tech = this.tech_; 47239 this.options_.externHls = Hls; 47240 47241 this.masterPlaylistController_ = new _masterPlaylistController.MasterPlaylistController(this.options_); 47242 this.playbackWatcher_ = new _playbackWatcher2['default'](_videoJs2['default'].mergeOptions(this.options_, { 47243 seekable: function seekable() { 47244 return _this3.seekable(); 47245 } 47246 })); 47247 47248 this.masterPlaylistController_.on('error', function () { 47249 var player = _videoJs2['default'].players[_this3.tech_.options_.playerId]; 47250 47251 player.error(_this3.masterPlaylistController_.error); 47252 }); 47253 47254 // `this` in selectPlaylist should be the HlsHandler for backwards 47255 // compatibility with < v2 47256 this.masterPlaylistController_.selectPlaylist = this.selectPlaylist ? this.selectPlaylist.bind(this) : Hls.STANDARD_PLAYLIST_SELECTOR.bind(this); 47257 47258 this.masterPlaylistController_.selectInitialPlaylist = Hls.INITIAL_PLAYLIST_SELECTOR.bind(this); 47259 47260 // re-expose some internal objects for backwards compatibility with < v2 47261 this.playlists = this.masterPlaylistController_.masterPlaylistLoader_; 47262 this.mediaSource = this.masterPlaylistController_.mediaSource; 47263 47264 // Proxy assignment of some properties to the master playlist 47265 // controller. Using a custom property for backwards compatibility 47266 // with < v2 47267 Object.defineProperties(this, { 47268 selectPlaylist: { 47269 get: function get() { 47270 return this.masterPlaylistController_.selectPlaylist; 47271 }, 47272 set: function set(selectPlaylist) { 47273 this.masterPlaylistController_.selectPlaylist = selectPlaylist.bind(this); 47274 } 47275 }, 47276 throughput: { 47277 get: function get() { 47278 return this.masterPlaylistController_.mainSegmentLoader_.throughput.rate; 47279 }, 47280 set: function set(throughput) { 47281 this.masterPlaylistController_.mainSegmentLoader_.throughput.rate = throughput; 47282 // By setting `count` to 1 the throughput value becomes the starting value 47283 // for the cumulative average 47284 this.masterPlaylistController_.mainSegmentLoader_.throughput.count = 1; 47285 } 47286 }, 47287 bandwidth: { 47288 get: function get() { 47289 return this.masterPlaylistController_.mainSegmentLoader_.bandwidth; 47290 }, 47291 set: function set(bandwidth) { 47292 this.masterPlaylistController_.mainSegmentLoader_.bandwidth = bandwidth; 47293 // setting the bandwidth manually resets the throughput counter 47294 // `count` is set to zero that current value of `rate` isn't included 47295 // in the cumulative average 47296 this.masterPlaylistController_.mainSegmentLoader_.throughput = { 47297 rate: 0, 47298 count: 0 47299 }; 47300 } 47301 }, 47302 /** 47303 * `systemBandwidth` is a combination of two serial processes bit-rates. The first 47304 * is the network bitrate provided by `bandwidth` and the second is the bitrate of 47305 * the entire process after that - decryption, transmuxing, and appending - provided 47306 * by `throughput`. 47307 * 47308 * Since the two process are serial, the overall system bandwidth is given by: 47309 * sysBandwidth = 1 / (1 / bandwidth + 1 / throughput) 47310 */ 47311 systemBandwidth: { 47312 get: function get() { 47313 var invBandwidth = 1 / (this.bandwidth || 1); 47314 var invThroughput = undefined; 47315 47316 if (this.throughput > 0) { 47317 invThroughput = 1 / this.throughput; 47318 } else { 47319 invThroughput = 0; 47320 } 47321 47322 var systemBitrate = Math.floor(1 / (invBandwidth + invThroughput)); 47323 47324 return systemBitrate; 47325 }, 47326 set: function set() { 47327 _videoJs2['default'].log.error('The "systemBandwidth" property is read-only'); 47328 } 47329 } 47330 }); 47331 47332 Object.defineProperties(this.stats, { 47333 bandwidth: { 47334 get: function get() { 47335 return _this3.bandwidth || 0; 47336 }, 47337 enumerable: true 47338 }, 47339 mediaRequests: { 47340 get: function get() { 47341 return _this3.masterPlaylistController_.mediaRequests_() || 0; 47342 }, 47343 enumerable: true 47344 }, 47345 mediaRequestsAborted: { 47346 get: function get() { 47347 return _this3.masterPlaylistController_.mediaRequestsAborted_() || 0; 47348 }, 47349 enumerable: true 47350 }, 47351 mediaRequestsTimedout: { 47352 get: function get() { 47353 return _this3.masterPlaylistController_.mediaRequestsTimedout_() || 0; 47354 }, 47355 enumerable: true 47356 }, 47357 mediaRequestsErrored: { 47358 get: function get() { 47359 return _this3.masterPlaylistController_.mediaRequestsErrored_() || 0; 47360 }, 47361 enumerable: true 47362 }, 47363 mediaTransferDuration: { 47364 get: function get() { 47365 return _this3.masterPlaylistController_.mediaTransferDuration_() || 0; 47366 }, 47367 enumerable: true 47368 }, 47369 mediaBytesTransferred: { 47370 get: function get() { 47371 return _this3.masterPlaylistController_.mediaBytesTransferred_() || 0; 47372 }, 47373 enumerable: true 47374 }, 47375 mediaSecondsLoaded: { 47376 get: function get() { 47377 return _this3.masterPlaylistController_.mediaSecondsLoaded_() || 0; 47378 }, 47379 enumerable: true 47380 } 47381 }); 47382 47383 this.tech_.one('canplay', this.masterPlaylistController_.setupFirstPlay.bind(this.masterPlaylistController_)); 47384 47385 this.masterPlaylistController_.on('selectedinitialmedia', function () { 47386 // Add the manual rendition mix-in to HlsHandler 47387 (0, _renditionMixin2['default'])(_this3); 47388 }); 47389 47390 // the bandwidth of the primary segment loader is our best 47391 // estimate of overall bandwidth 47392 this.on(this.masterPlaylistController_, 'progress', function () { 47393 this.tech_.trigger('progress'); 47394 }); 47395 47396 // In the live case, we need to ignore the very first `seeking` event since 47397 // that will be the result of the seek-to-live behavior 47398 this.on(this.masterPlaylistController_, 'firstplay', function () { 47399 this.ignoreNextSeekingEvent_ = true; 47400 }); 47401 47402 this.tech_.ready(function () { 47403 return _this3.setupQualityLevels_(); 47404 }); 47405 47406 // do nothing if the tech has been disposed already 47407 // this can occur if someone sets the src in player.ready(), for instance 47408 if (!this.tech_.el()) { 47409 return; 47410 } 47411 47412 this.tech_.src(_videoJs2['default'].URL.createObjectURL(this.masterPlaylistController_.mediaSource)); 47413 } 47414 47415 /** 47416 * Initializes the quality levels and sets listeners to update them. 47417 * 47418 * @method setupQualityLevels_ 47419 * @private 47420 */ 47421 }, { 47422 key: 'setupQualityLevels_', 47423 value: function setupQualityLevels_() { 47424 var _this4 = this; 47425 47426 var player = _videoJs2['default'].players[this.tech_.options_.playerId]; 47427 47428 if (player && player.qualityLevels) {
vendor: 1,476 bytes, lines 47429-47488
47429 this.qualityLevels_ = player.qualityLevels(); 47430 47431 this.masterPlaylistController_.on('selectedinitialmedia', function () { 47432 handleHlsLoadedMetadata(_this4.qualityLevels_, _this4); 47433 }); 47434 47435 this.playlists.on('mediachange', function () { 47436 handleHlsMediaChange(_this4.qualityLevels_, _this4.playlists); 47437 }); 47438 } 47439 } 47440 47441 /** 47442 * Begin playing the video. 47443 */ 47444 }, { 47445 key: 'play', 47446 value: function play() { 47447 this.masterPlaylistController_.play(); 47448 } 47449 47450 /** 47451 * a wrapper around the function in MasterPlaylistController 47452 */ 47453 }, { 47454 key: 'setCurrentTime', 47455 value: function setCurrentTime(currentTime) { 47456 this.masterPlaylistController_.setCurrentTime(currentTime); 47457 } 47458 47459 /** 47460 * a wrapper around the function in MasterPlaylistController 47461 */ 47462 }, { 47463 key: 'duration', 47464 value: function duration() { 47465 return this.masterPlaylistController_.duration(); 47466 } 47467 47468 /** 47469 * a wrapper around the function in MasterPlaylistController 47470 */ 47471 }, { 47472 key: 'seekable', 47473 value: function seekable() { 47474 return this.masterPlaylistController_.seekable(); 47475 } 47476 47477 /** 47478 * Abort all outstanding work and cleanup. 47479 */ 47480 }, { 47481 key: 'dispose', 47482 value: function dispose() { 47483 if (this.playbackWatcher_) { 47484 this.playbackWatcher_.dispose(); 47485 } 47486 if (this.masterPlaylistController_) { 47487 this.masterPlaylistController_.dispose(); 47488 }
47489 if (this.qualityLevels_) { 47490 this.qualityLevels_.dispose(); 47491 } 47492 _get(Object.getPrototypeOf(HlsHandler.prototype), 'dispose', this).call(this); 47493 } 47494 }]); 47495 47496 return HlsHandler; 47497})(Component); 47498 47499var HlsSourceHandler = function HlsSourceHandler(mode) { 47500 return { 47501 canHandleSource: function canHandleSource(srcObj) { 47502 var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; 47503 47504 var localOptions = _videoJs2['default'].mergeOptions(_videoJs2['default'].options, options); 47505 47506 // this forces video.js to skip this tech/mode if its not the one we have been 47507 // overriden to use, by returing that we cannot handle the source. 47508 if (localOptions.hls && localOptions.hls.mode && localOptions.hls.mode !== mode) { 47509 return false; 47510 } 47511 return HlsSourceHandler.canPlayType(srcObj.type, localOptions); 47512 }, 47513 handleSource: function handleSource(source, tech) { 47514 var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; 47515 47516 var localOptions = _videoJs2['default'].mergeOptions(_videoJs2['default'].options, options, { hls: { mode: mode } }); 47517 47518 if (mode === 'flash') { 47519 // We need to trigger this asynchronously to give others the chance 47520 // to bind to the event when a source is set at player creation 47521 tech.setTimeout(function () { 47522 tech.trigger('loadstart'); 47523 }, 1); 47524 } 47525 47526 tech.hls = new HlsHandler(source, tech, localOptions); 47527 tech.hls.xhr = (0, _xhr2['default'])(); 47528 47529 tech.hls.src(source.src); 47530 return tech.hls; 47531 }, 47532 canPlayType: function canPlayType(type) { 47533 var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; 47534 47535 var localOptions = _videoJs2['default'].mergeOptions(_videoJs2['default'].options, options); 47536 47537 if (HlsSourceHandler.canPlayType(type, localOptions)) { 47538 return 'maybe'; 47539 } 47540 return ''; 47541 } 47542 }; 47543}; 47544 47545HlsSourceHandler.canPlayType = function (type, options) { 47546 // No support for IE 10 or below 47547 if (_videoJs2['default'].browser.IE_VERSION && _videoJs2['default'].browser.IE_VERSION <= 10) { 47548 return false; 47549 } 47550 47551 var mpegurlRE = /^(audio|video|application)\/(x-|vnd\.apple\.)?mpegurl/i; 47552 47553 // favor native HLS support if it's available 47554 if (!options.hls.overrideNative && Hls.supportsNativeHls) { 47555 return false; 47556 } 47557 return mpegurlRE.test(type); 47558}; 47559 47560if (typeof _videoJs2['default'].MediaSource === 'undefined' || typeof _videoJs2['default'].URL === 'undefined') { 47561 _videoJs2['default'].MediaSource = _videojsContribMediaSources.MediaSource; 47562 _videoJs2['default'].URL = _videojsContribMediaSources.URL; 47563} 47564 47565var flashTech = _videoJs2['default'].getTech('Flash'); 47566 47567// register source handlers with the appropriate techs 47568if (_videojsContribMediaSources.MediaSource.supportsNativeMediaSources()) { 47569 _videoJs2['default'].getTech('Html5').registerSourceHandler(HlsSourceHandler('html5'), 0); 47570} 47571if (_globalWindow2['default'].Uint8Array && flashTech) { 47572 flashTech.registerSourceHandler(HlsSourceHandler('flash')); 47573} 47574 47575_videoJs2['default'].HlsHandler = HlsHandler; 47576_videoJs2['default'].HlsSourceHandler = HlsSourceHandler; 47577_videoJs2['default'].Hls = Hls; 47578if (!_videoJs2['default'].use) { 47579 _videoJs2['default'].registerComponent('Hls', Hls); 47580} 47581_videoJs2['default'].m3u8 = _m3u8Parser2['default']; 47582_videoJs2['default'].options.hls = _videoJs2['default'].options.hls || {}; 47583 47584if (_videoJs2['default'].registerPlugin) { 47585 _videoJs2['default'].registerPlugin('reloadSourceOnError', _reloadSourceOnError2['default']); 47586} else { 47587 _videoJs2['default'].plugin('reloadSourceOnError', _reloadSourceOnError2['default']); 47588} 47589 47590module.exports = { 47591 Hls: Hls, 47592 HlsHandler: HlsHandler, 47593 HlsSourceHandler: HlsSourceHandler 47594}; 47595}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 47596},{"./bin-utils":2,"./config":3,"./master-playlist-controller":5,"./playback-watcher":8,"./playlist":11,"./playlist-loader":9,"./playlist-selectors.js":10,"./reload-source-on-error":13,"./rendition-mixin":14,"./xhr":21,"aes-decrypter":25,"global/document":31,"global/window":32,"m3u8-parser":33,"videojs-contrib-media-sources":74}]},{},[77])(77) 47597}); 47598!function e(t,n,o){function i(r,u){if(!n[r]){if(!t[r]){var f="function"==typeof require&&require;if(!u&&f)return f(r,!0);if(l)return l(r,!0);var d=new Error("Cannot find module '"+r+"'");throw d.code="MODULE_NOT_FOUND",d}var s=n[r]={exports:{}};t[r][0].call(s.exports,function(e){var n=t[r][1][e];return i(n?n:e)},s,s.exports,e,t,n,o)}return n[r].exports}for(var l="function"==typeof require&&require,r=0;r<o.length;r++)i(o[r]);return i}({1:[function(e,t,n){(function(t){"use strict";
47598function o(e){return e&&e.__esModule?e:{"default":e}}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function l(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function r(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}n.__esModule=!0;var u="undefined"!=typeof window?window.videojs:"undefined"!=typeof t?t.videojs:null,f=o(u),d=e(4),s=o(d),a=e(2),c=o(a),p=function(e){function t(){var n;i(this,t);var o=l(this,e.call(this)),r=o;if(f["default"].browser.IS_IE8){r=s["default"].createElement("custom");for(var u in t.prototype)"constructor"!==u&&(r[u]=t.prototype[u])}return r.levels_=[],r.selectedIndex_=-1,Object.defineProperty(r,"selectedIndex",{get:function(){return r.selectedIndex_}}),Object.defineProperty(r,"length",{get:function(){return r.levels_.length}}),n=r,l(o,n)}return r(t,e),t.prototype.addQualityLevel=function(e){var t=this.getQualityLevelById(e.id);if(t)return t;var n=this.levels_.length;return t=new c["default"](e),""+n in this||Object.defineProperty(this,n,{get:function(){return this.levels_[n]}}),this.levels_.push(t),this.trigger({qualityLevel:t,type:"addqualitylevel"}),t},t.prototype.removeQualityLevel=function(e){for(var t=null,n=0,o=this.length;n<o;n++)if(this[n]===e){t=this.levels_.splice(n,1)[0],this.selectedIndex_===n?this.selectedIndex_=-1:this.selectedIndex_>n&&this.selectedIndex_--;break}return t&&this.trigger({qualityLevel:e,type:"removequalitylevel"}),t},t.prototype.getQualityLevelById=function(e){for(var t=0,n=this.length;t<n;t++){var o=this[t];if(o.id===e)return o}return null},t.prototype.dispose=function(){this.selectedIndex_=-1,this.levels_.length=0},t}(f["default"].EventTarget);p.prototype.allowedEvents_={change:"change",addqualitylevel:"addqualitylevel",removequalitylevel:"removequalitylevel"};for(var y in p.prototype.allowedEvents_)p.prototype["on"+y]=null;n["default"]=p}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],2:[function(e,t,n){(function(t){"use strict";function o(e){return e&&e.__esModule?e:{"default":e}}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}n.__esModule=!0;var l="undefined"!=typeof window?window.videojs:"undefined"!=typeof t?t.videojs:null,r=o(l),u=e(4),f=o(u),d=function s(e){i(this,s);var t=this;if(r["default"].browser.IS_IE8){t=f["default"].createElement("custom");for(var n in s.prototype)"constructor"!==n&&(t[n]=s.prototype[n])}return t.id=e.id,t.label=t.id,t.width=e.width,t.height=e.height,t.bitrate=e.bandwidth,t.enabled_=e.enabled,Object.defineProperty(t,"enabled",{get:function(){return t.enabled_()},set:function(e){t.enabled_(e)}}),t};n["default"]=d}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],3:[function(e,t,n){},{}],4:[function(e,t,n){(function(n){var o="undefined"!=typeof n?n:"undefined"!=typeof window?window:{},i=e(3);if("undefined"!=typeof document)t.exports=document;else{var l=o["__GLOBAL_DOCUMENT_CACHE@4"];l||(l=o["__GLOBAL_DOCUMENT_CACHE@4"]=i),t.exports=l}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],5:[function(e,t,n){(function(t){"use strict";function o(e){return e&&e.__esModule?e:{"default":e}}n.__esModule=!0;var i="undefined"!=typeof window?window.videojs:"undefined"!=typeof t?t.videojs:null,l=o(i),r=e(1),u=o(r),f=l["default"].registerPlugin||l["default"].plugin,d=function(e,t){var n=e.qualityLevels,o=new u["default"],i=function l(){o.dispose(),e.qualityLevels=n,e.off("dispose",l)};return e.on("dispose",i),e.qualityLevels=function(){return o},e.qualityLevels.VERSION="2.0.3",o},s=function(e){return d(this,l["default"].mergeOptions({},e))};
47598f("qualityLevels",s),s.VERSION="2.0.3",n["default"]=s}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}]},{},[5]); 47599// Monkey-patch our way to greater glory!!! 47600(function () { 47601 47602 // Shim Object.create if our browser is too old and crusty 47603 if (typeof Object['create'] !== 'function') { 47604 Object['create'] = function (o) { 47605 function F() {} 47606 F.prototype = o; 47607 return new F(); 47608 }; 47609 } 47610 47611 function setTracking() { 47612 this.one('ended', onEndedOnce); 47613 this.one('play', onPlayOnce); 47614 this.on('timeupdate', onProgressOnce); 47615 } 47616 47617 // custom logic applied when "ready" is triggered on any player 47618 var player = window['videojs'].getComponent('Player'), 47619 triggerReady = player.prototype.triggerReady; 47620 player.prototype.triggerReady = function () { 47621 setTracking.call(this); 47622 // trigger the normal "ready" events 47623 triggerReady.call(this); 47624 }; 47625 47626 // We will be hanging on to the native vjsPlayer 47627 var vjsPlayer = window['videojs']; 47628 // ... and hack that constructor! 47629 function monkeyPlayer() { 47630 vjsPlayer.apply(this, arguments); 47631 // Wire in Google Analytics tracking on any event we can think of 47632 this.one('ended', onEndedOnce); 47633 this.one('play', onPlayOnce); 47634 this.on('timeupdate', onProgressOnce); 47635 47636 } 47637 // Borrow the prototype 47638 monkeyPlayer.prototype = Object.create(vjsPlayer.prototype); 47639 // Throw any static properties on there as well 47640 for (var key in vjsPlayer) { 47641 if (vjsPlayer.hasOwnProperty(key)) { 47642 monkeyPlayer[key] = vjsPlayer[key]; 47643 } 47644 } 47645 // Keep the native vjsPlayer just for good measure 47646 monkeyPlayer.vjsPlayer = vjsPlayer; 47647 // ... and patch that videojs! 47648 window['videojs']['Player'] = monkeyPlayer; 47649 47650 // Function definitions 47651 // (these were monkey patched in the constructor in case you didn't notice) 47652 function onEndedOnce(e) { 47653 _gaq_push(['_trackEvent', 'Videos', 'Video Completed', getVideoId(this)]); 47654 } 47655 function onPlayOnce(e) { 47656 _gaq_push(['_trackEvent', 'Videos', 'Play', getVideoId(this)]); 47657 } 47658 function onProgressOnce(e) { 47659 var progress; 47660 try { 47661 // Sometimes duration() errors, so we will not try to send this 47662 // event if we cannot tell when to send it. 47663 progress = this.currentTime() / this.duration(); 47664 } catch (e) { 47665 return; 47666 } 47667 if (progress >= 0.2) { 47668 this.off('timeupdate', onProgressOnce); 47669 this.on('timeupdate', onProgressTwice); 47670 _gaq_push(['_trackEvent', 'Videos', 'Watched 20%', getVideoId(this)]); 47671 } 47672 } 47673 function onProgressTwice(e) { 47674 var progress; 47675 try { 47676 // Sometimes duration() errors, so we will not try to send this 47677 // event if we cannot tell when to send it. 47678 progress = this.currentTime() / this.duration(); 47679 } catch (e) { 47680 return; 47681 } 47682 if (progress >= 0.8) { 47683 this.off('timeupdate', onProgressTwice); 47684 _gaq_push(['_trackEvent', 'Videos', 'Watched 80%', getVideoId(this)]); 47685 } 47686 } 47687 47688 // For convenience 47689 function _gaq_push() { 47690 if (window._tr) window._tr.push.apply(_tr, arguments);
47691 if (window._gaq) return window._gaq.push.apply(_gaq, arguments); 47692 if (window.console) console.log.apply(console, arguments); 47693 } 47694 function getVideoId(vid) { 47695 var src = vid.currentSrc(); 47696 47697 var hlsParts = vid.currentSrc().match(/([^/_]*)_.*[.]csmil\/master[.]m3u8$/); 47698 47699 if(hlsParts && hlsParts.length >= 2 ) { 47700 return hlsParts[1]; 47701 } 47702 47703 47704 var parts = src.match(/\?v=([^&]*)/); 47705 if (parts) return parts[1]; 47706 // default to the filename at the end of the path 47707 parts = src.match(/[^\/]*$/); 47708 if (parts) return parts[0]; 47709 // give up and return the src 47710 return src; 47711 } 47712 47713})(); 47714/** 47715@file 47716Generic JS util functions useful for the responsive slideshow. 47717No dependencies. 47718Should be included after the slideshow DOM, as early as possible. 47719*/ 47720 47721 47722//<script> 47723 47724if (!window.responsiveSliderDetermineFirstSlide) { 47725 47726 47727 47728 function responsiveSliderDetermineFirstSlide() { 47729 47730 if (window.globalSlideshowOptionsOverride && globalSlideshowOptionsOverride.startSlide) { 47731 return globalSlideshowOptionsOverride.startSlide; 47732 } 47733 47734 47735 if (!document.querySelectorAll || !Array.prototype.reduce) return; 47736 47737 47738 if (globalGetParams.video || globalGetParams['video-play']) { 47739 47740 var videoParam = globalGetParams.video || globalGetParams['video-play']; 47741 var mySlides = document.querySelectorAll('#header .slideshow-main slide'); 47742 var slideMatching = Array.prototype.reduce.call( 47743 document.querySelectorAll('#header .slideshow-main slide .video-js'), 47744 function(m, v) { 47745 if (m != null) return m; 47746 var parentSlide = window._getParentForType && _getParentForType(v, 'SLIDE'); 47747 for (var i = 0; i < mySlides.length; i++) { 47748 if (mySlides[i] == parentSlide) { 47749 if (_getVideoIdForSlide(mySlides, i) == videoParam) return i; 47750 break; 47751 } 47752 } 47753 }, 47754 null 47755 ); 47756 47757 if (slideMatching !== void 0) return parseInt(slideMatching) + 1; 47758 47759 47760 if (globalGetParams.slide) return globalGetParams.slide; 47761 47762 47763 return 1; 47764 47765 } 47766 47767 if (location.hash) { 47768 var newSlideIdx = parseInt(location.hash.substring(1).replace(/slide/, '')) - 1; 47769 if (!isNaN(newSlideIdx) && newSlideIdx >= 0 && newSlideIdx <= document.querySelectorAll('#header slide').length - 1) { 47770 return newSlideIdx + 1; 47771 } 47772 } 47773 47774 47775 return 1; 47776 47777 } 47778 47779} 47780 47781if (!window._slideToSlideId) { 47782 47783 47784 function _slideToSlideId(slidesIdx) { 47785 var deltaX = (isRTL ? -1 : 1) * (slidesIdx-1) * 100; 47786 var slideHolder = document.querySelector('#header .slideshow-main .slide-holder'); 47787 if (!slideHolder) return; 47788 setTransform(slideHolder, 'translate3d(calc(' + (-deltaX) + '% - ' + (deltaX / 2) + 'px), 0, 0)'); 47789 } 47790 47791} 47792 47793if (!window.setTransform) { 47794 47795 47796 47797 function setTransform(elem, transform) { 47798 elem.style.MozTransform = transform; 47799 elem.style.msTransform = transform; 47800 elem.style.OTransform = transform; 47801 elem.style.WebkitTransform = transform; 47802 elem.style.transform = transform; 47803 } 47804 47805} 47806 47807if (!window.cookieMonster) { 47808 47809 47810 47811 var cookieMonster = { 47812 get: function (sKey) { 47813 return decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || null; 47814 }, 47815 set: function (sKey, sValue, vEnd, sPath, sDomain, bSecure) { 47816 if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) { return false; } 47817 var sExpires = ""; 47818 if (vEnd) { 47819 switch (vEnd.constructor) { 47820 case Number: 47821 sExpires = vEnd === Infinity ? "; expires=Fri, 31 Dec 9999 23:59:59 GMT" : "; max-age=" + vEnd; 47822 break; 47823 case String: 47824 sExpires = "; expires=" + vEnd; 47825 break; 47826 case Date: 47827 sExpires = "; expires=" + vEnd.toUTCString(); 47828 break; 47829 } 47830 } 47831 document.cookie = encodeURIComponent(sKey) + "=" + encodeURIComponent(sValue) + sExpires + (sDomain ? "; domain=" + sDomain : "") + (sPath ? "; path=" + sPath : "") + (bSecure ? "; secure" : ""); 47832 return true; 47833 }, 47834 remove: function (sKey, sPath, sDomain) { 47835 if (!sKey || !this.has(sKey)) { return false; } 47836 document.cookie = encodeURIComponent(sKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT" + ( sDomain ? "; domain=" + sDomain : "") + ( sPath ? "; path=" + sPath : ""); 47837 return true; 47838 }, 47839 has: function (sKey) { 47840 return (new RegExp("(?:^|;\\s*)" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test(document.cookie); 47841 } 47842 }; 47843 47844} 47845 47846 47847function slideshowCaptionsOverflow() { 47848 47849 47850 if (document.body.className.match(/authoring-true/)) return; 47851 47852 47853 47854 47855 if(document.documentElement.lang == "zh" && document.location.pathname == "/scientology-today/events/new-years-2017.html") { 47856 47857 return true; 47858 } 47859 47860 [].forEach.call(document.querySelectorAll('#header_slideshow_wraper .slide-templated-new .item.text-item'), function(v) { 47861 47862 if (v.className.match(/caption-overflow-adjusted/)) { 47863 v.classList.remove('caption-overflow-adjusted'); 47864 var full = v.parentNode.querySelector('.slider-captions-more-full'); 47865 47866 if (full) { 47867 v.innerHTML = full.innerHTML; 47868 $(full).parent('slide').find('.text-item').show(); 47869 full.parentNode.removeChild(full); 47870 v.offsetTop; 47871 } 47872 } 47873 47874 if (v.offsetHeight < v.scrollHeight) { 47875 47876 var origCont = v.innerHTML, 47877 tempSlideCaptionAdjusted= false; 47878 47879 for (var infiniteGuard = 200; infiniteGuard > 0; infiniteGuard--) { 47880 47881 47882 47883 var cont = v.innerHTML.replace(new RegExp('... <a[^>]*>' + (window.moreTexti18n ? moreTexti18n : 'more') + ' »<.a>.*$'), ''); 47884 47885 47886 47887 cont = cont.replace(/&[a-z]+;$/, ''); 47888 47889 47890 var lastDivider = Math.max(cont.lastIndexOf(' '), cont.lastIndexOf('.'), cont.lastIndexOf(','), cont.lastIndexOf('ã'), cont.lastIndexOf('ã'), cont.lastIndexOf('ã»')); 47891 47892 if (lastDivider < 1) { 47893 47894 window.console && console.error && console.error('could not break this string: ' + origCont); 47895 v.innerHTML = origCont; 47896 break; 47897 } 47898 47899 var newCont = cont.substring(0, lastDivider); 47900 47901 47902 var tempElem = document.createElement('DIV'); 47903 tempElem.innerHTML = newCont; 47904 newCont = tempElem.innerHTML; 47905 47906 if (cont == newCont) { 47907 window.console && console.error && console.error('no change:'); 47908 window.console && console.error && console.error(cont); 47909 window.console && console.error && console.error(newCont); 47910 break; 47911 } 47912 47913 v.innerHTML = newCont + '... <a href="#" class="slider-captions-more">' + (window.moreTexti18n ? moreTexti18n : 'more') + ' »</a>'; 47914 47915 47916 v.offsetTop; 47917 if (v.offsetHeight >= v.scrollHeight) { 47918 v.classList.add('caption-overflow-adjusted'); 47919 47920 v.insertAdjacentHTML('afterend', '<span class="slider-captions-more-full ignore-zoom item">' + origCont + '<div class="close-full-caption">'+closeTexti18n+'<span class="glyphicon glyphicon-remove-circle"></span></div></span>'); 47921 tempSlideCaptionAdjusted = true; 47922 break; 47923 } 47924 47925 }; 47926 47927 if (!tempSlideCaptionAdjusted) 47928 window.console && console.error && console.error('could not break this string: ' + origCont); 47929 47930 } 47931 47932 }); 47933} 47934 47935 47936 47937 47938 47939 47940 47941 47942 47943 47944 47945 47946 47947 47948 47949 47950var effectivePixelRatio = 1; 47951if (window.devicePixelRatio) { 47952 if (window.devicePixelRatio > 1.0 && window.devicePixelRatio <= 1.5) { 47953 effectivePixelRatio = 1.5; 47954 } else if (window.devicePixelRatio > 1.5) { 47955 47956 effectivePixelRatio = 2; 47957 } 47958} 47959 47960 47961var deviceCustomType = 'tablet'; 47962if (screen.height > 1024 || screen.width > 1024 || /Trident/.test(navigator.userAgent)) { 47963 deviceCustomType = 'desktop'; 47964} else if (screen.height < 768 && screen.width < 768) { 47965 deviceCustomType = 'mobile'; 47966} 47967 47968if (document.documentElement) document.documentElement.className += ' device-custom-type-' + deviceCustomType; 47969 47970 47971function determineImageSize(imageDom) { 47972 47973 if (!imageDom.getAttribute('data-height')) { 47974 if(window.location.pathname == "/") { 47975 return [[800, 450], [2*800, 2*450]]; 47976 } 47977 47978 window.console && console.error && console.error('no data-height specified:'); 47979 window.console && console.error && console.error(imageDom && imageDom.outerHTML); 47980 47981 47982 return [[860, 575], [2*860, 2*575]]; 47983 } 47984 47985 var imageRelativeDims = [parseInt(imageDom.getAttribute('data-width')) / 860, parseInt(imageDom.getAttribute('data-height')) / 575]; 47986 47987 47988 if (deviceCustomType == 'desktop' && effectivePixelRatio == 1) { 47989 return [ 47990 [Math.round(imageRelativeDims[0] * 860), Math.round(imageRelativeDims[1] * 575)], 47991 47992 47993 [Math.round(imageRelativeDims[0] * 860), Math.round(imageRelativeDims[1] * 575)] 47994 ]; 47995 } 47996 47997 47998 if (deviceCustomType == 'desktop') { 47999 return [ 48000 [Math.round(imageRelativeDims[0] * 860 * effectivePixelRatio), Math.round(imageRelativeDims[1] * 575 * effectivePixelRatio)], 48001 [Math.round(imageRelativeDims[0] * 860 * effectivePixelRatio), Math.round(imageRelativeDims[1] * 575 * effectivePixelRatio)] 48002 ]; 48003 } 48004 48005 48006 if (deviceCustomType == 'tablet') { 48007 return [ 48008 [Math.round(imageRelativeDims[0] * 860 * effectivePixelRatio), Math.round(imageRelativeDims[1] * 575 * effectivePixelRatio)], 48009 [Math.round(imageRelativeDims[0] * 860 * Math.max(1.5, effectivePixelRatio)), Math.round(imageRelativeDims[1] * 575 * Math.max(1.5, effectivePixelRatio))] 48010 ]; 48011 } 48012 48013 48014 return [ 48015 48016 [Math.round(imageRelativeDims[0] * 680 * effectivePixelRatio), Math.round(imageRelativeDims[1] * 454 * effectivePixelRatio)], 48017 [Math.round(imageRelativeDims[0] * 680 * effectivePixelRatio), Math.round(imageRelativeDims[1] * 454 * effectivePixelRatio)] 48018 ]; 48019 48020} 48021 48022function determineImageSourcesFromPrebuilt(imageDom, imgSizes) { 48023 if (!imageDom || !imageDom.getAttribute || !window.JSON || !JSON.parse) return; 48024 48025 var serializedSources = imageDom.getAttribute('data-prebuilt-responsive-sources'); 48026 if (!serializedSources) return; 48027 48028 try { 48029 var responsiveSources = JSON.parse(serializedSources); 48030 var primarySrc = responsiveSources[String(imgSizes[0][0])]; 48031 if (!primarySrc) return; 48032 48033 return [primarySrc, responsiveSources[String(imgSizes[1][0])] || primarySrc]; 48034 } catch (err) { 48035 window.console && console.error && console.error('bad prebuilt responsive sources:'); 48036 window.console && console.error && console.error(serializedSources); 48037 } 48038} 48039 48040 48041function determineImageSources(imageDom) { 48042 48043 if (!imageDom || !imageDom.getAttribute || !imageDom.getAttribute('data-src')) { 48044 window.console && console.error && console.error('cannot get image source, no data-src specified:'); 48045 window.console && console.error && console.error(imageDom); 48046 return; 48047 } 48048 48049 var imgSrc = imageDom.getAttribute('data-src'); 48050 var qaOverride = getParameterByName('qa'); 48051 if (qaOverride) { 48052 imgSrc = imgSrc.replace(/@qa=85/, '@qa='+qaOverride); 48053 } 48054 48055 48056 if(imgSrc.indexOf('overrideW')===-1) { 48057 imgSrc = imgSrc.replace(/@w=[0-9\.]+/, ''); 48058 } 48059 imgSrc = imgSrc.replace(/@h=[0-9\.]+/, ''); 48060 48061 48062 var imgSizes = determineImageSize(imageDom); 48063 var prebuiltSources = determineImageSourcesFromPrebuilt(imageDom, imgSizes); 48064 if (prebuiltSources) return prebuiltSources; 48065 48066 if (imgSrc.match(/cropfit@/)) { 48067 return [imgSrc.replace(/cropfit@/, 'cropfit@w=' + imgSizes[0][0] + '@'), imgSrc.replace(/cropfit@/, 'cropfit@w=' + imgSizes[1][0] + '@')]; 48068 } else if (imgSrc.match(/cropfit/)) { 48069 return [imgSrc.replace(/cropfit/, 'cropfit@w=' + imgSizes[0][0]), imgSrc.replace(/cropfit/, 'cropfit@w=' + imgSizes[1][0])]; 48070 } else { 48071 window.console && console.error && console.error('non-cropfit image: ' + imgSrc); 48072 return [imgSrc, imgSrc.replace(/temp-color-tests/, 'temp-color-tests/retina')]; 48073 } 48074 48075} 48076 48077 48078 48079function slideshowPreloadFirstSlide() { 48080 48081 48082 48083 48084 48085 document.querySelector && new function() { 48086 48087 var firstImage = document.querySelector('slide img[data-src]'); 48088 var firstSlide = document.querySelector('slide'); 48089 48090 if (window.responsiveSliderDetermineFirstSlide) { 48091 var firstSlideNum = responsiveSliderDetermineFirstSlide(); 48092 firstSlide = document.querySelector('slide:nth-child(' + (firstSlideNum+1) + ')'); 48093 firstImage = document.querySelector('slide:nth-child(' + (firstSlideNum+1) + ') img[data-src]'); 48094 } 48095 48096 48097 if (firstSlide) firstSlide.className += ' fso-slide-active ';
48098 48099 if (firstImage) { 48100 48101 if (firstSlide.querySelector('.image-holder') && window.determineImageSources) { 48102 48103 firstSlide.className += ' js-starting-slide'; 48104 48105 [].forEach.call(firstSlide.querySelectorAll('.image-holder'), function(imgHolder) { 48106 48107 48108 48109 48110 48111 imgHolder.className += ' js-dynamic-size-set'; 48112 imgHolder.style.height = (Math.ceil(parseFloat(imgHolder.getAttribute('data-height')) / 575 * 1000000) / 10000) + '%'; 48113 imgHolder.style.width = (Math.ceil(parseFloat(imgHolder.getAttribute('data-width')) / 860 * 1000000) / 10000) + '%'; 48114 48115 48116 var theImage = imgHolder.querySelector('img'); 48117 theImage.addEventListener && theImage.addEventListener('load', function() { 48118 imgHolder.classList && imgHolder.classList.remove('js-preview'); 48119 }); 48120 theImage.src = determineImageSources(theImage)[0]; 48121 theImage.className += ' loaded'; 48122 48123 48124 48125 }); 48126 48127 if (firstSlide.querySelectorAll('.image-holder') && firstSlide.querySelectorAll('.image-holder').length == 1) firstSlide.className += ' js-single-image-slide'; 48128 48129 document.querySelector('.slideshow-wrapper').className += ' js-slideshow-preinited'; 48130 48131 } else { 48132 [].forEach.call(firstSlide.querySelectorAll('img'), function(theImage) { 48133 theImage.src = theImage.getAttribute('data-src'); 48134 48135 48136 48137 48138 }); 48139 48140 } 48141 } 48142 } 48143 48144} 48145 48146 48147 48148 48149 48150 48151function slideshowPrepare() { 48152 48153 48154 if (document.querySelectorAll && document.querySelectorAll('.slide-holder slide').length == 1) 48155 document.getElementById('header_slideshow_wraper').classList.add('slideshow-only-one-slide'); 48156 48157 48158 if (window.responsiveSliderDetermineFirstSlide) { 48159 var firstSlide = responsiveSliderDetermineFirstSlide(); 48160 _slideToSlideId(firstSlide); 48161 } 48162 48163 48164 if (document.querySelector && !document.querySelector('.slide-holder slide')) 48165 document.querySelector('.slideshow-wrapper.slideshow-main').parentNode.removeChild(document.querySelector('.slideshow-wrapper.slideshow-main')); 48166 48167 48168 if ( 48169 window.deviceCustomType == 'desktop' && 48170 document.body.className.match(/templated-slider-build-2-1/) && 48171 (window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) > 767 && 48172 ( 48173 (window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) < 1025 || 48174 !document.body.className.match(/locale-en/) || 48175 document.body.className.match(/path-scientology-today-church-openings/) 48176 || document.body.className.match(/path-inside-our-church/) 48177 ) && 48178 window.slideshowCaptionsOverflow 48179 ) { 48180 slideshowCaptionsOverflow(); 48181 } 48182 48183} 48184function getParameterByName(name, url) { 48185 if (!url) url = window.location.href; 48186 name = name.replace(/[\[\]]/g, "\\$&"); 48187 var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), 48188 results = regex.exec(url); 48189 if (!results) return null; 48190 if (!results[2]) return ''; 48191 return decodeURIComponent(results[2].replace(/\+/g, " ")); 48192} 48193 48194 48195//</script> 48196 48197 48198// polyfill 48199if (!window.logger) { 48200 logger = function(){}; 48201 logger.debug = function(){}; 48202} 48203 48204// @from http://stackoverflow.com/questions/5227909/get-element-padding-value-using-javascript 48205if (!window.getStyle) { 48206 function getStyle(oElm, strCssRule) { 48207 var strValue = ""; 48208 if (document.defaultView && document.defaultView.getComputedStyle){ 48209 strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule); 48210 } 48211 else if (oElm.currentStyle){ 48212 strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){ 48213 return p1.toUpperCase(); 48214 }); 48215 strValue = oElm.currentStyle[strCssRule]; 48216 } 48217 return strValue; 48218 } 48219} 48220 48221// wire up the total control over touches for an element (class) 48222// Note: can be called before $(document).ready and doesn't require jQuery 48223if (!window.wireupTotalTouchControl) { 48224 function wireupTotalTouchControl(aClass, aCallback, aTransparent) { 48225 // console.log("Hey, 2"); 48226 48227 // if (!globalIsIOS) return; 48228 if (!globalIsMobile) return; 48229 for (var elem in {'touchstart': 1, 'touchmove': 1, 'touchcancel': 1}) { 48230 document.addEventListener(elem, function(e) { 48231 48232 if (e && e.target && e.target.classList && 48233 (e.target.classList.contains(aClass) || e.target.parentNode.classList.contains(aClass))) { 48234 if (!aTransparent) { 48235 e.stopPropagation(); 48236 e.stopImmediatePropagation(); 48237 e.preventDefault(); 48238 return false;
48239 } else { 48240 return true; 48241 } 48242 } 48243 48244 return true; 48245 48246 }, true); 48247 } 48248 48249 document.addEventListener('touchend', function(e) { 48250 48251 if (e && e.target && e.target.classList && 48252 (e.target.classList.contains(aClass) || e.target.parentNode.classList.contains(aClass))) { 48253 if (!aTransparent) { 48254 aCallback(e); 48255 e.stopPropagation(); 48256 e.stopImmediatePropagation(); 48257 e.preventDefault(); 48258 return false; 48259 } else { 48260 return aCallback(e); 48261 } 48262 } 48263 48264 return true; 48265 48266 }, true); 48267 48268 } 48269} 48270 48271 48272 48273 48274 48275//<script> 48276 48277 48278 48279 48280 48281 48282 48283 48284 48285 48286 48287 48288 48289 48290 48291function _next_elem(that) { 48292 var nextElem = $(that).next(); 48293 48294 if ($.Q && $.Q.widows) { 48295 48296 } 48297 48298 48299 48300 if ($(nextElem).attr('tagName') == null) { 48301 return null; 48302 } 48303 48304 if ($(nextElem).attr('tagName').toLowerCase() != 'p') { 48305 return null; 48306 } 48307 48308 48309 48310 if (nextElem.css('clear') == 'both' || 48311 (nextElem.css('clear') == 'left') ) { 48312 return null; 48313 48314 } 48315 48316 return nextElem; 48317 48318} 48319 48320 48321 48322function _is_para_empty(that) { 48323 48324 var myTrimmedContent = $(that).html(); 48325 var regExp = new RegExp("\\n", "g"); 48326 myTrimmedContent = myTrimmedContent.replace(regExp, ''); 48327 myTrimmedContent = myTrimmedContent.replace("/ */", ''); 48328 48329 if (myTrimmedContent == '') { 48330 return true; 48331 } 48332 48333 if (myTrimmedContent == ' ') { 48334 return true; 48335 } 48336 48337 return false; 48338 48339} 48340 48341 48342 48343function _gcui_pwidow_nuke() { 48344 48345 var minParaWidth = 100; 48346 var minLineLength = 30; 48347 var maxWordLength = 30; 48348 var pElemTypes = ['.content p', '.slide-holder .text-item']; 48349 48350 for (var j in pElemTypes) { 48351 48352 48353 if ($(pElemTypes[j]).length == 0) { 48354 continue; 48355 } 48356 $(pElemTypes[j]).each(function() { 48357 48358 if ($(this).hasClass('skip-widows') || $(this).parents('.skip-widows').length) return; 48359 48360 var initWidth = $(this).width(); 48361 var myContent = $(this).html(); 48362 48363 myContent = myContent.replace(/[ \n\r]*$/g, ''); 48364 if (initWidth > minParaWidth && myContent.length > minLineLength) { 48365 48366 48367 var lastSpace = myContent.lastIndexOf(' '); 48368 var lastWord = myContent.substr(lastSpace + 1); 48369 var contBeforeBR = ''; 48370 48371 48372 48373 48374 48375 var beforeLastWord = ''; 48376 var oneBeforeLastSpace = myContent.substr(1, lastSpace-1).lastIndexOf(' '); 48377 if (oneBeforeLastSpace != -1) { 48378 beforeLastWord = myContent.substr(oneBeforeLastSpace + 2, lastSpace - oneBeforeLastSpace - 1); 48379 } 48380 48381 if (lastWord.length <= maxWordLength && lastWord.length + beforeLastWord.length <= maxWordLength) { 48382 48383 48384 if (lastWord.indexOf(' ') == -1 && lastWord.indexOf('>') == -1 && lastWord.indexOf('>') == -1) { 48385 var newString = myContent.substr(0, lastSpace) + ' ' + myContent.substr(lastSpace+1); 48386 $(this).html(newString); 48387 48388 48389 if ($(this).width() > initWidth) { 48390 $(this).html(myContent); 48391 } 48392 48393 } 48394 } 48395 48396 } 48397 }); 48398 } 48399 48400} 48401 48402$(window).ready(function() { 48403 _gcui_pwidow_nuke(); 48404}); 48405 48406$(window).load(function() { 48407 48408 48409 48410 48411 48412 var elemTypes = [ ['.gcui-inline-image', 100] ]; 48413 48414 var widowCount = 0; 48415 for (var i in elemTypes) { 48416 48417 48418 if ($(elemTypes[i][0]).length == 0) { 48419 continue; 48420 } 48421 48422 $(elemTypes[i][0]).each(function() { 48423 48424 if (window.globalGetParams && globalGetParams.widows) { 48425 $(this).css('border', '1px dashed red'); 48426 } 48427 48428 48429 var myBottom = $(this).position().top + $(this).outerHeight(true); 48430 48431 48432 var nextElem = _next_elem(this); 48433 if (nextElem != null) { 48434 var nextBottom = $(nextElem).position().top + $(nextElem).outerHeight(true); 48435 } 48436 while (nextElem != null && nextBottom <= myBottom) { 48437 48438 if (window.globalGetParams && globalGetParams.widows && $(nextElem).css('border') == '1px solid green') { 48439 $(nextElem).css('border', 'none'); 48440 } 48441 nextElem = _next_elem(nextElem); 48442 if (nextElem == null) { 48443 break; 48444 } 48445 nextBottom = nextElem.position().top + nextElem.outerHeight(true); 48446 } 48447 48448 48449 48450 if (nextElem != null) { 48451 if (window.globalGetParams && globalGetParams.widows) { 48452 $(this).css('border', '1px dashed yellow'); 48453 } 48454 if (nextBottom - myBottom < elemTypes[i][1] && !_is_para_empty(nextElem) ) { 48455 48456 48457 if ($(this).attr('class').indexOf('widowfix') == -1) { 48458 $(nextElem).addClass('auto-widow-' + widowCount); 48459 $(this).addClass('widowfix[.' + 'auto-widow-' + widowCount + ']'); 48460 widowCount++; 48461 } 48462 48463 if (window.globalGetParams && globalGetParams.widows) { 48464 $(this).css('border', '1px dashed blue'); 48465 } 48466 } 48467 } 48468 48469 }); 48470 48471 } 48472 48473 48474 48475 var doAdjust = function() { 48476 48477 var isIE6 = navigator.userAgent.toLowerCase().indexOf('msie
484776') != -1; 48478 48479 $("*[class*=widowfix]").each(function() { 48480 48481 var myClass = $(this).attr('class'); 48482 48483 48484 48485 if (myClass.indexOf('widowfix[') < 0) { return; } 48486 48487 var myTargetSelector = myClass.split('[')[1].split(']')[0]; 48488 var myTarget = $(myTargetSelector); 48489 var myTargetY = myTarget.position().top + myTarget.outerHeight(); 48490 if ($(this).css('margin-top') == 'auto' || $(this).css('margin-top') == 'default') { 48491 var myMarginTop = 0; 48492 } else { 48493 var myMarginTop = parseInt($(this).css('margin-top').replace(/px/, '')) + 0; 48494 } 48495 if ($(this).css('margin-bottom') == 'auto' || $(this).css('margin-bottom') == 'default') { 48496 var myMarginBottom = 0; 48497 } else { 48498 var myMarginBottom = parseInt($(this).css('margin-bottom').replace(/px/, '')) + 0; 48499 } 48500 var myHeightProperty = 'min-height'; 48501 48502 if (isIE6 || $(this).attr('tagName').toLowerCase() == 'table') { 48503 myHeightProperty = 'height'; 48504 } 48505 if ((myTargetY - $(this).position().top - myMarginTop - myMarginBottom) > 0) { 48506 $(this).css(myHeightProperty, (myTargetY - $(this).position().top - myMarginTop - myMarginBottom) + 'px'); 48507 } 48508 48509 }); 48510 } 48511 48512 48513 48514 setTimeout(doAdjust, 50); 48515 setTimeout(doAdjust, 100); 48516 setTimeout(doAdjust, 150); 48517 setTimeout(doAdjust, 200); 48518 48519}); 48520 48521 48522 48523 48524//</script> 48525 48526 48527 48528var VideoController = function(opts) { 48529 // mix in the opts 48530 if (opts) for (var key in opts) this[key] = opts[key]; 48531} 48532 48533VideoController.prototype = (function() { 48534 48535 var me = {}; 48536 48537 var autogeneratedId = 0; 48538 48539 /** 48540 * Initialize the video player (videoJS element). Starts to download the video and 48541 * starts to play it as well if `aVideoId` == true. 48542 * 48543 * @param aDomElem 48544 * DOM element to use for the video player (jQuery object is OK as well). 48545 * @param aOpts 48546 * Video.js options (e.g. autoplay, controls, good defaults: { controls: true, autoplay: true } 48547 * @return 48548 * videoJS object (which has methods like .on(...) and stuff like that) 48549 */ 48550 me.init = function(aDomElem, aOpts) { 48551 48552 // spare casperjs from our videos ... 48553 if (globalGetParams && globalGetParams.headless) return { 48554 on: function() {}, 48555 one: function() {}, 48556 trigger: function() {}, 48557 el: function() {}, 48558 posterImage: { 48559 show: function(){} 48560 } 48561 }; 48562 48563 var $elem = (aDomElem instanceof jQuery) ? aDomElem : $(aDomElem), 48564 vid = (aDomElem instanceof jQuery) ? aDomElem[0] : $elem[0]; 48565 48566 var domElemId = vid.id; 48567 48568 // if DOM elem has no ID, auto-genereate one for it 48569 if (!domElemId) { 48570 domElemId = 'gsc_video_controller_' + autogeneratedId++; 48571 vid.id = domElemId; 48572 } 48573 48574 logger.debug('videoJS DOM elem ID: ' + domElemId); 48575 48576 // HACK android hack 48577 if ($('html').hasClass('device-android')) { 48578 48579 $elem.attr('controls', 'controls'); 48580 48581 // polyfill some of the event listeners 48582 vid.on = vid.addEventListener; 48583 vid.one = function(aEvent, aFunc) { 48584 var theFunc = function() { 48585 aFunc.apply(this, arguments); 48586 vid.removeEventListener(aEvent, theFunc); 48587 }; 48588 vid.addEventListener(aEvent, theFunc); 48589 } 48590 vid.trigger = function () { 48591 $elem.trigger.apply($elem, arguments); 48592 } 48593 vid.el = function() { 48594 return vid; 48595 } 48596 vid.posterImage = { 48597 show: function(){ 48598 vid.load(); 48599 } 48600 } 48601 48602 } else { 48603 48604 var opts = aOpts || { controls: true, autoplay: false }; 48605 48606 // Temporary fix for broken mp4 files in Firefox's HTML5 player 48607 if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1 && 48608 navigator.platform.indexOf("Win") > -1 && 48609 $elem.find('source').eq(0).attr('type') != 'video/webm') { 48610 opts.techOrder = ["hls", "html5", "flash"]; 48611 } 48612 48613 vid = videojs(domElemId, opts); 48614 48615 if (opts.showPosterOnVideoEnnd) { 48616 48617 vid.on('ended', function() { 48618 48619 // cancel out fullscreen 48620 try { vid.cancelFullScreen(); } catch (swallow) { } // HACK errors on iPhone and iPad 48621 48622 // show the poster image 48623 vid.posterImage.show(); 48624 48625 // reset the video to the beginning 48626 if (typeof vid.currentTime === 'function') vid.currentTime(0); 48627 48628 }); 48629 48630 } 48631 48632 } 48633 48634 return vid; 48635 48636 } 48637 48638 return me; 48639 48640})(); 48641 48642// set the videoJS strings once 48643$(document).ready(function() { 48644 48645 videojs.options.strings = { 48646 'en': { 48647 'Play': 'Play', 48648 'Pause': 'Pause', 48649 'Mute': 'Mute', 48650 'Unmute': 'Mute', 48651 'Current Time': 'Current Time', 48652 'Duration Time': 'Duration Time', 48653 'Fullscreen': 'Fullscreen',
48654 'Non-Fullscreen': 'Non-Fullscreen' 48655 } 48656 }; 48657 48658 videojs.options.language = 'en'; 48659 videojs.options.flash.swf = '/theme/swf/video-js.swf'; 48660 48661 if (!$('html').hasClass('device-ipad')) { 48662 $('[data-gsc-setup]').each(function() { 48663 //globalVideoController.init($(this), $(this).data('gscSetup')); 48664 }); 48665 } 48666 48667}); 48668 48669 48670globalVideoController = new VideoController(); 48671 48672 48673 48674//<script> 48675 48676 48677 48678 48679if (!window.loadJS) { 48680 48681 48682 function loadJS(src, cb) { 48683 var ref = window.document.getElementsByTagName( "script" )[ 0 ]; 48684 var script = window.document.createElement( "script" ); 48685 script.src = src; 48686 script.async = true; 48687 ref.parentNode.insertBefore( script, ref ); 48688 if (cb && typeof(cb) === "function") { 48689 script.onload = cb; 48690 } 48691 return script; 48692 } 48693} 48694 48695var GscResponsiveSlider = function(opts) { 48696 48697 for (var key in opts) this[key] = opts[key]; 48698} 48699 48700GscResponsiveSlider.prototype = (function() { 48701 48702 var me = {}; 48703 48704 48705 48706 48707 48708 me.domParent = '#header'; 48709 me.fsoSliderZoomHeight = 575; 48710 var fsoSlideWidth = 860; 48711 48712 48713 48714 48715 48716 48717 me.addControls = true; 48718 me.autoplayVideos = true; 48719 me.circularClone = false; 48720 me.slidesMax860 = false; 48721 me.animationGracePeriod = 1000; 48722 48723 48724 48725 48726 48727 var $mySlides, 48728 animationGracePeriod, 48729 totalSlides, 48730 fsoSliderTop, 48731 slideholderDelta = 0; 48732 48733 var ipAnimation = 0; 48734 var $mySlide, 48735 48736 myDirection, 48737 extraMotion; 48738 48739 var fsoSlideWidth2; 48740 48741 var fsoSliderZoomTimeout = 0; 48742 var fsoSliderZoomedIn = false; 48743 48744 var fsoSliderResizeTimer = 0; 48745 48746 var $allNonImageElems, 48747 $allImageElems; 48748 var xRatio = 1; 48749 var attrs = ['top', 'bottom', 'left', 'right', 'width', 'height']; 48750 var dotHolderWidth; 48751 48752 var currentActiveSlideIdx = 0; 48753 var slideshowScrollTop, 48754 fsoSliderZoomedIn, 48755 lastSlideOffset, 48756 containerWideEnoughForNoSliding; 48757 48758 var allZoomedInAppleDots, 48759 allNormalAppleDots, 48760 myTransform, 48761 mySlideholderElem, 48762 tooManyAppleDots, 48763 midwaySlideNumLow = 0, 48764 midwaySlideNum = 0; 48765 48766 var isIpad; 48767 48768 var textAnimationQueue = []; 48769 48770 48771 var $slideshowMainElem, 48772 slideshowMainElem, 48773 $slideSelectorElem, 48774 slideSelectorElem, 48775 $slideArrowHolder, 48776 slideArrowHolder, 48777 $slideArrowLeft, 48778 slideArrowLeft, 48779 $slideHolderDom, 48780 slideHolderDom; 48781 48782 var customLeftPanel = false; 48783 48784 var isCloned = false; 48785 48786 var $mySlideshowHolder, 48787 $mySlideshowWrapper, 48788 plusOne, 48789 minusOne; 48790 48791 48792 48793 48794 me.init = function() { 48795 48796 48797 48798 48799 if (window.globalLang && globalLang == "he") { 48800 plusOne = -1; 48801 minusOne = 1; 48802 } else { 48803 plusOne = 1; 48804 minusOne = -1; 48805 } 48806 48807 var that = this; 48808 48809 if (!(this.domParent instanceof jQuery)) { 48810 this.domParent = $(this.domParent); 48811 } 48812 48813 48814 $slideshowMainElem = this.domParent.find('.slideshow-main'); 48815 slideshowMainElem = $slideshowMainElem[0]; 48816 if (!slideshowMainElem) return logger.error('There is no ".slideshow-main"!'); 48817 if (!(this.domParent.find('.slide-wrapper')[0])) return logger.error('There is no ".slide-wrapper" on ".slideshow-main"!'); 48818 48819 48820 if (this.touchMode === void 0 && this.domParent.find('.video-slide').length) { 48821 this.touchMode = 'default'; 48822 if (window.deviceCustomType != 'desktop') this.zoomEnabled = false; 48823 } 48824 48825 if (this.touchMode === void 0) this.touchMode = 'default'; 48826 48827 48828 if (window.deviceCustomType != 'desktop' && this.touchMode == 'photoswipe') this.zoomEnabled = false; 48829 48830 if (this.zoomEnabled && !this.addControls) return logger.error('Conflicting options (zoomEnabled && !addControls): how am I supposed to enable zoom without adding controls?', 'error'); 48831 48832 48833 if (this.zoomOnClick === void 0) { 48834 if ($slideshowMainElem.find('slide .video-js').length) { 48835 this.zoomOnClick = false;
48836 logger.debug('setting zoomOnClick to false'); 48837 } else { 48838 this.zoomOnClick = true; 48839 logger.debug('setting zoomOnClick to true'); 48840 } 48841 } 48842 if (this.hasLeftSidebar === void 0) { 48843 if (!this.domParent.find('.slideshow-left-nav').length) { 48844 this.hasLeftSidebar = false; 48845 logger.debug('setting hasLeftSidebar to false'); 48846 } else { 48847 this.hasLeftSidebar = true; 48848 logger.debug('setting hasLeftSidebar to true'); 48849 } 48850 } 48851 if (this.useSimpleSliding === void 0) { 48852 if ($slideshowMainElem.find('slide .item:not(.left-panel-mobile-caption)').length) { 48853 this.useSimpleSliding = false; 48854 this.slidesMax860 = (this.slidesMax860 === void 0 ? true : this.slidesMax860); 48855 logger.debug('setting useSimpleSliding to false'); 48856 } else { 48857 this.useSimpleSliding = true; 48858 logger.debug('setting useSimpleSliding to true'); 48859 } 48860 } 48861 if (this.zoomEnabled === void 0) { 48862 if (!$slideshowMainElem.find('slide .item:not(.left-panel-mobile-caption)').length) { 48863 this.zoomEnabled = false; 48864 logger.debug('setting zoomEnabled to false'); 48865 } else { 48866 this.zoomEnabled = true; 48867 logger.debug('setting zoomEnabled to true'); 48868 } 48869 } 48870 48871 $mySlideshowHolder = this.domParent.find('.slideshow-holder'); 48872 $mySlideshowWrapper = this.domParent.find('.slideshow-wrapper'); 48873 48874 $slideHolderDom = this.domParent.find('.slide-holder'); 48875 slideHolderDom = $slideHolderDom[0]; 48876 if (!slideHolderDom) return logger.error('There is no ".slide-holder"!'); 48877 48878 if (this.domParent.find('slide').length === 1) { 48879 $mySlideshowWrapper.addClass('slideshow-single-slide'); 48880 } 48881 48882 var tempThis = this; 48883 48884 48885 48886 48887 48888 if (window.deviceCustomType != 'desktop' && this.touchMode == 'photoswipe') { 48889 48890 48891 loadJS('/theme/photoswipe/photoswipe.min.js?_=9d8f7badc'); 48892 loadJS('/theme/photoswipe/photoswipe-ui-default.min.js?_=9d8f7badc'); 48893 48894 48895 window.galleryItems = []; 48896 48897 $('slide').each(function() { 48898 48899 48900 48901 48902 var $clone = $(this).clone(); 48903 $clone.find('.image-holder').each(function() { 48904 48905 $(this).css('width', (Math.round(parseFloat($(this).attr('data-width')) / 860 * 1000000) / 10000) + '%'); 48906 $(this).css('height', (Math.round(parseFloat($(this).attr('data-height')) / 575 * 1000000) / 10000) + '%'); 48907 }); 48908 48909 if ($clone.find('.image-holder').length == 1) { 48910 $clone.addClass('js-single-image-slide'); 48911 var $imgHolder = $clone.find('.image-holder').eq(0); 48912 var slideDims = [window.innerWidth, window.innerWidth * 0.6686]; 48913 var imageDims = [window.innerWidth * 0.6686 * parseFloat($imgHolder.attr('data-width')) / parseFloat($imgHolder.attr('data-height')), window.innerWidth * 0.6686]; 48914 48915 48916 if (imageDims[0] > $imgHolder.attr('data-width') ) { 48917 imageDims[0] = $imgHolder.attr('data-width'); 48918 } 48919 48920 if(window.location.pathname != "/scientology-today/church-openings/grand-opening-scientology-media-productions.html" && window.location.pathname != "/churches/bringing-scientology-world/scientology-media-productions.html") { 48921 $imgHolder.css('min-width', Math.floor(imageDims[0]) + 'px'); 48922 } 48923 48924 } 48925 48926 $clone.find('img[data-src]').each(function() { 48927 var imgSrc = determineImageSources(this); 48928 48929 if (imgSrc) $(this).attr('srca', imgSrc[1]).removeAttr('data-src').removeAttr('src'); 48930 }); 48931 48932 48933 var theSlideHtml = $clone[0].outerHTML; 48934 if (!theSlideHtml.match(/js-preview/)) theSlideHtml = theSlideHtml.replace(/image-holder/g, 'image-holder js-preview'); 48935 48936 var slideObj = { 48937 html: theSlideHtml.replace(/srca=/g, 'src='), 48938 } 48939 if (deviceCustomType == 'mobile') slideObj.title = $clone.find('.text-item').html(); 48940 galleryItems.push(slideObj); 48941 48942 }); 48943 48944 $('.pswp')[0].className += ' zoomedIn slide-wrapper bs-slideshow'; 48945 document.body.className += ' photoswipe-on slideshow-zoomed-in'; 48946 $('.pswp .pswp__container').html('<div class="pswp__item"></div> <div class="pswp__item"></div> <div class="pswp__item"></div>'); 48947 48948 } 48949 48950 48951 if ( 48952 document.body.className.match(/templated-slider-build-2-1/) && 48953 (window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) > 767 && 48954 ( 48955 (window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) < 1025 || 48956 !document.body.className.match(/locale-en/) 48957 ) && 48958 window.slideshowCaptionsOverflow 48959 ) { 48960 slideshowCaptionsOverflow(); 48961 } 48962 48963 if (this.domParent.find('slide').length === 0) window.console && console.error && console.error('ERROR: slideshow has no slides!'); 48964 48965 48966 48967
48968 if (this.addControls) { 48969 $mySlideshowHolder.append('\ 48970 <div class="slide-controls">\ 48971 <div class="slide-arrow-holder slide-left-arrow2">\ 48972 <div class="arrow-bg"></div>\ 48973 <arrow></arrow>\ 48974 </div>\ 48975 <div class="slide-arrow-holder slide-right-arrow2">\ 48976 <div class="arrow-bg"></div>\ 48977 <arrow></arrow>\ 48978 </div>\ 48979 </div>\ 48980 <div class="slide-selectors"></div>\ 48981 '); 48982 if (this.zoomEnabled) { 48983 $mySlideshowHolder.append('\ 48984 <div class="slide-zoomer">\ 48985 <span class="click-to-expand">Expand</span>\ 48986 </div>\ 48987 '); 48988 $mySlideshowWrapper.before('\ 48989 <div class="fso-custom-slider-overlay fso-custom-slider-fader"></div>\ 48990 <div class="fso-slide-upper-bg fso-custom-slider-fader">\ 48991 <a href="#" class="closebtn"><span class="glyphicon glyphicon-remove-circle"></span></a>\ 48992 </div>\ 48993 <div class="fso-slide-lower-bg fso-custom-slider-fader">\ 48994 <div class="fso-slide-selectors slide-selectors"></div>\ 48995 </div>\ 48996 <div class="fso-custom-zoomer">\ 48997 <div class="slide-zoomer">\ 48998 <span class="click-to-expand">Close</span>\ 48999 </div>\ 49000 </div>\ 49001 '); 49002 } 49003 $slideHolderDom.after('\ 49004 <div class="fso-slide-mobile-numbers">\ 49005 <span class="slide-num"></span> / <span class="slide-total"></span>\ 49006 </div>\ 49007 '); 49008 } 49009 49010 $slideSelectorElem = this.domParent.find('.slide-selectors'); 49011 slideSelectorElem = $slideSelectorElem[0]; 49012 49013 if (!slideSelectorElem && this.addControls) return logger.error('There is no ".slide-selectors"!'); 49014 49015 $slideArrowHolder = this.domParent.find('.slide-arrow-holder'); 49016 slideArrowHolder = $slideArrowHolder[0]; 49017 if (!slideArrowHolder) return logger.error('There is no ".slide-arrow-holder"!'); 49018 49019 $slideArrowLeft = this.domParent.find('.slide-left-arrow2'); 49020 slideArrowLeft = $slideArrowLeft[0]; 49021 if (!slideArrowLeft) return logger.error('There is no left arrow: ".slide-left-arrow2"!'); 49022 49023 if (!(this.domParent.find('.slide-controls')[0])) return logger.error('There is no ".slide-controls"!'); 49024 if (!(this.domParent.find('.slide-right-arrow2')[0])) return logger.error('There is no ".slide-right-arrow2"!'); 49025 49026 $mySlides = this.domParent.find('.slide-holder slide'); 49027 animationGracePeriod = this.animationGracePeriod; 49028 totalSlides = this.domParent.find('slide').length; 49029 $allNonImageElems = $mySlides.find('div.item,h3.item,.inactive-dot'); 49030 $allImageElems = $mySlides.find('img.item'); 49031 myMinSlideLeft = -10 - (fsoSlideWidth * (totalSlides - 1)); 49032 slideshowScrollTop = slideshowMainElem.offsetTop; 49033 49034 myTransform = Modernizr.prefixed('transform'); 49035 mySlideholderElem = this.domParent.find('.slide-holder')[0]; 49036 49037 isIpad = $('html').hasClass('device-ipad'); 49038 simpleAnimation = this.useSimpleSliding; 49039 49040 isCloned = this.circularClone; 49041 49042 49043 if ($('html').hasClass('device-iphone') || globalIsIE) isCloned = this.circularClone = false; 49044 if (globalIsIOS) isCloned = this.circularClone = false; 49045 49046 if (isCloned) { 49047 $mySlideshowWrapper.addClass('slideshow-been-cloned'); 49048 } else { 49049 $mySlideshowWrapper.addClass('slideshow-not-cloned'); 49050 } 49051 49052 customLeftPanel = !! this.domParent.find('.custom-slide-leftnav').length; 49053 49054 49055 49056 49057 49058 49059 49060 if (this.domParent.find('.slide-stretcher').length) { 49061 var paddingBottom = getStyle(this.domParent.find('.slide-stretcher')[0], "padding-bottom"); 49062 if (paddingBottom) { 49063 var stretcherPaddingBottom = parseInt(paddingBottom.replace(/px/, '')); 49064 if (!isNaN(stretcherPaddingBottom)) { 49065 var stretchPercent = stretcherPaddingBottom / this.domParent.find('.slide-stretcher').width() * 100; 49066 this.fsoSliderZoomHeight = fsoSlideWidth * stretchPercent / 100; 49067 49068 } 49069 } 49070 } 49071 49072 49073 $(document).on('click', '.new-age-caption-overlay,.video-slide-overlay.center-playbutton-overlay', function(e) { 49074 var videlem = $(this).siblings('.video-js'); 49075 playVideoJS(videlem); 49076 fireEvent('userInteraction', 'video-play'); 49077 }); 49078 49079 if (!this.domParent.find('.fso-slide-active')) $mySlides.eq(0).addClass('fso-slide-active'); 49080 49081 49082 if (totalSlides > 30 && this.domParent.find('.slide-selectors').length) { 49083 tooManyAppleDots = true; 49084 $('body').addClass('fso-slider-too-many-apple-dots'); 49085 } else { 49086 tooManyAppleDots = false;
49087 $('body').addClass('fso-slider-not-too-many-apple-dots'); 49088 } 49089 49090 49091 49092 49093 49094 49095 49096 for (var i = 0; i < totalSlides; i++) { 49097 (function() { 49098 49099 var $thisSlide = $mySlides.eq(i); 49100 49101 $thisSlide.find('.image-holder').each(function() { 49102 49103 if ($(this).hasClass('js-dynamic-size-set')) return; 49104 $(this).addClass('js-dynamic-size-set').css('width', (Math.round(parseFloat($(this).attr('data-width')) / 860 * 1000000) / 10000) + '%'); 49105 $(this).css('height', (Math.round(parseFloat($(this).attr('data-height')) / 575 * 1000000) / 10000) + '%'); 49106 49107 }); 49108 49109 if (window.deviceCustomType == 'desktop' || tempThis.touchMode != 'photoswipe') { 49110 $thisSlide.find('.image-holder img[data-src]').each(function() { 49111 var tempSize = determineImageSources(this); 49112 if (tempSize && tempSize[1] != tempSize[0]) { 49113 $(this).after($(this).clone().removeAttr('src').removeAttr('data-src').attr('data-double-src', tempSize[1]).addClass('zoomer-image')); 49114 } 49115 }); 49116 49117 49118 if ($thisSlide.hasClass('js-starting-slide')) { 49119 $thisSlide.addClass('double-loaded').find('.zoomer-image').each(function() { 49120 $(this).attr('src', $(this).attr('data-double-src')).addClass('loaded'); 49121 }); 49122 } 49123 49124 } else { 49125 49126 if ($thisSlide.find('.image-holder').length == 1) $thisSlide.addClass('js-single-image-slide'); 49127 49128 } 49129 49130 })(); 49131 } 49132 49133 49134 49135 49136 var tempFirstSlide = this.determineFirstSlide(); 49137 if (window.deviceCustomType == 'desktop' || tempThis.touchMode != 'photoswipe') loadSurroundingSlides(Math.max(0, tempFirstSlide-1)); 49138 49139 49140 if (!globalIsIOS && !globalIsSafari) { 49141 for (var i = 0; i < totalSlides; i++) { 49142 (function() { 49143 49144 if (window.deviceCustomType != 'desktop' && tempThis.touchMode == 'photoswipe') return; 49145 49146 var $thisSlide = $mySlides.eq(i); 49147 setTimeout(function() { 49148 $thisSlide.find('.image-item[data-src]:not(.loaded),.image-holder img[data-src]:not(.loaded)').each(function() { 49149 49150 var $myThisSlide = $thisSlide; 49151 var $this = $(this); 49152 var imgSrc = $this.attr('data-src'); 49153 49154 49155 if ($this.attr('data-height')) imgSrc = determineImageSources(this)[0]; 49156 49157 logger.debug('preloading #1: ' + imgSrc); 49158 49159 if ($this.attr('src') != imgSrc) { 49160 $this.on('load', function() { 49161 $this.parents('.image-holder').removeClass('js-preview'); 49162 }); 49163 $this.css('background', 'none').addClass('loading-image-hide') 49164 .attr('src', imgSrc).addClass('loaded'); 49165 } else { 49166 49167 49168 $this.css('background', 'none') 49169 .addClass('loaded'); 49170 $this.parents('.image-holder').removeClass('js-preview'); 49171 } 49172 49173 $this.load(function() { 49174 $this.removeClass('loading-image-hide'); 49175 }); 49176 }); 49177 }, 500 * i); 49178 })(); 49179 } 49180 } 49181 49182 49183 49184 49185 49186 if (this.circularClone) { 49187 49188 logger.debug('cloning slides and sheep'); 49189 var slideRefs = []; 49190 49191 $mySlides.each(function() { 49192 49193 slideRefs.push(this); 49194 var $cloned = $(this).clone(); 49195 49196 49197 49198 $cloned.find('video').each(function() { 49199 var myPoster = $(this).attr('poster'); 49200 if (!myPoster) return; 49201 var posterDom = document.createElement("img"); 49202 posterDom.src = myPoster; 49203 posterDom.style.position = 'absolute'; 49204 this.parentNode.replaceChild(posterDom, this); 49205 if (this.parentNode && this.parentNode.tagName && this.parentNode.tagName.toLowerCase() === 'div') this.parentNode.style.position = 'absolute'; 49206 }); 49207 49208 49209 $cloned.find('[id]').each(function() { 49210 $(this).attr('id', $(this).attr('id') + '_cloned_after'); 49211 }); 49212 if ($cloned.attr('id')) $cloned.attr('id', $cloned.attr('id') + '_cloned_after'); 49213 49214 49215 if ($cloned.attr('class')) { 49216 $cloned.attr('class', $cloned.attr('class').replace(/fso-slide-([0-9]*)/, function(match1) { 49217 return match1 + '_cloned_after'; 49218 })); 49219 } 49220 49221
49222 if ($cloned.attr('data-slide-id')) { 49223 $cloned.attr('data-slide-id', $cloned.attr('data-slide-id') + '_cloned_after'); 49224 } 49225 49226 $cloned.appendTo($slideHolderDom); 49227 49228 }); 49229 49230 for (var i = slideRefs.length - 1; i >= 0; i--) { 49231 49232 var $cloned = $(slideRefs[i]).clone(); 49233 49234 49235 49236 49237 $cloned.find('video').each(function() { 49238 var myPoster = $(this).attr('poster'); 49239 if (!myPoster) return; 49240 var posterDom = document.createElement("img"); 49241 posterDom.src = myPoster; 49242 posterDom.style.position = 'absolute'; 49243 this.parentNode.replaceChild(posterDom, this); 49244 }); 49245 49246 49247 $cloned.find('[id]').each(function() { 49248 $(this).attr('id', $(this).attr('id') + '_cloned_before'); 49249 }); 49250 if ($cloned.attr('id')) $cloned.attr('id', $cloned.attr('id') + '_cloned_before'); 49251 49252 49253 if ($cloned.attr('class')) { 49254 $cloned.attr('class', $cloned.attr('class').replace(/fso-slide-([0-9]*)/, function(match1) { 49255 return match1 + '_cloned_before'; 49256 })); 49257 } 49258 49259 49260 if ($cloned.attr('data-slide-id')) { 49261 $cloned.attr('data-slide-id', $cloned.attr('data-slide-id') + '_cloned_before'); 49262 } 49263 49264 $cloned.prependTo($slideHolderDom); 49265 49266 }; 49267 49268 } 49269 49270 49271 49272 49273 49274 49275 49276 49277 49278 49279 49280 49281 var dots_html = '', thumb_class; 49282 for (var i = 0; i < totalSlides; i++) { 49283 dots_html += '<a class="slide-link" href="#slide' + (i+1) + '"><div class="slide-thumb'; 49284 if (thumb_class = $mySlides.eq(i).attr('data-slide-thumb')) { 49285 dots_html += ' ' + thumb_class; 49286 } 49287 if (thumb_class = $mySlides.eq(i).attr('data-slide-thumb-style')) { 49288 dots_html += '" style="' + thumb_class; 49289 } 49290 dots_html += '"><div class="slide-thumb-image"></div></div></a> '; 49291 } 49292 $slideSelectorElem.addClass('scrolling-dots').html('\ 49293 <div class="slide-dot-over slide-dot-over-left">\ 49294 <a class="slide-first-link"></a>\ 49295 <a class="slide-prev-next slide-prev" href="#">Prev</a>\ 49296 </div>\ 49297 <div class="slide-dots-holder">\ 49298 <div class="slide-dots-holder-outer">\ 49299 <div class="slide-dots-holder-inner">' + dots_html + '</div>\ 49300 </div>\ 49301 </div>\ 49302 <div class="slide-dot-over slide-dot-over-right">\ 49303 <a class="slide-prev-next slide-next" href="#">Next</a>\ 49304 <a class="slide-last-link"></a>\ 49305 </div>\ 49306 '); 49307 49308 $('.slide-dot-over.slide-dot-over-left').eq(1).width($('.slide-prev-next.slide-prev').eq(1).width() + 27); 49309 $('.slide-dot-over.slide-dot-over-right').eq(1).width($('.slide-prev-next.slide-next').eq(1).width() + 37); 49310 49311 if (this.zoomEnabled) { 49312 allZoomedInAppleDots = this.domParent.find('.slide-dots-holder').eq(0).find('.slide-link'); 49313 allNormalAppleDots = this.domParent.find('.slide-dots-holder').eq(1).find('.slide-link'); 49314 } else { 49315 allNormalAppleDots = this.domParent.find('.slide-dots-holder').eq(0).find('.slide-link'); 49316 } 49317 49318 var tempPos = allNormalAppleDots.eq(totalSlides - 1).position(); 49319 if (!tempPos) { 49320 49321 if (this.addControls) 49322 window.console && console.error && console.error('ERROR: slider dot error!'); 49323 } else { 49324 lastSlideOffset = allNormalAppleDots.eq(totalSlides - 1).position().left; 49325 if (isRTL) lastSlideOffset = allNormalAppleDots.eq(0).position().left - lastSlideOffset; 49326 } 49327 49328 49329 this.domParent.find('.slide-link').on('click', function() { 49330 fireEvent('userInteraction', 'click-apple-dot'); 49331 var myLink = $(this).attr('href'); 49332 if (myLink[0] != '#') return true; 49333 replaceHash(myLink.substring(1)); 49334 return false;
49335 }); 49336 49337 $slideArrowHolder.on('click', function(e) { 49338 49339 e.preventDefault(); 49340 e.stopPropagation(); 49341 49342 var isLeftArrow = $(this).hasClass('slide-left-arrow2'); 49343 49344 if (isLeftArrow) { 49345 sliderMovePrev(); 49346 } 49347 if (!isLeftArrow) { 49348 that.sliderMoveNext(); 49349 } 49350 49351 49352 49353 49354 fireEvent('userInteraction', 'arrow-click'); 49355 49356 return false; 49357 }); 49358 49359 this.domParent.find('.fso-slide-mobile-numbers .slide-total').text(totalSlides); 49360 this.domParent.find('.fso-slide-mobile-numbers .slide-num').text(1); 49361 49362 $(window).on('hashchange', function() { 49363 fsoSliderHashChange(); 49364 }); 49365 49366 49367 49368 49369 var myFirstSlide = this.determineFirstSlide(); 49370 if (myFirstSlide === 1) { 49371 49372 49373 $mySlideshowHolder.addClass('slider-first-slide'); 49374 $slideshowMainElem.attr('data-slide-active', 0); 49375 this.slideHasChanged(); 49376 49377 49378 if (this.autoplayVideos && $mySlides.eq(0).find('.video-js').length) startVideoForSlide(0); 49379 49380 } else { 49381 49382 if (!isIpad) { 49383 49384 fsoSliderGotoSlide(myFirstSlide - 1, !($slideshowMainElem.hasClass('js-slideshow-preinited'))); 49385 49386 49387 if (this.autoplayVideos && $mySlides.eq(myFirstSlide - 1).find('.video-js').length) startVideoForSlide(myFirstSlide - 1); 49388 49389 } else { 49390 fsoSliderGotoSlide(myFirstSlide - 1, false); 49391 sliderPositionSlides(true); 49392 } 49393 49394 } 49395 49396 this.domParent.find('.fso-slide-mobile-numbers').css('opacity', 1); 49397 49398 this.domParent.find('.slide-prev').on('click', function(e) { 49399 e.preventDefault(); 49400 sliderMovePrev(); 49401 fireEvent('userInteraction', 'prev-click'); 49402 }); 49403 49404 this.domParent.find('.slide-next').on('click', function(e) { 49405 e.preventDefault(); 49406 that.sliderMoveNext(); 49407 fireEvent('userInteraction', 'next-click'); 49408 }); 49409 49410 49411 49412 49413 49414 49415 49416 49417 if (this.zoomEnabled) { 49418 49419 $('#pre_banner_wrapper').addClass('fso-zoomer'); 49420 $('#addthis').addClass('fso-zoomer'); 49421 $('#banner').addClass('fso-zoomer'); 49422 $('#content_container').addClass('fso-zoomer'); 49423 $('#admin-menu').addClass('fso-zoomer'); 49424 49425 if (this.zoomOnClick) { 49426 $slideHolderDom.on('click', function(e) { 49427 if ($('body').hasClass('editing')) return true; 49428 if ($('body').hasClass('slide-captions-only')) return true; 49429 49430 49431 if (!globalIsMobile && e && e.target && $(e.target).attr('class') && ($(e.target).attr('class').indexOf('vjs') >= 0 || $(e.target).parent().attr('class').indexOf('templated-video-slide') >= 0 ) ) return true; 49432 if (e && e.target && $(e.target).hasClass('ignore-zoom')) return true; 49433 if (e && e.target && $(e.target).parents('.ignore-zoom').length) return true; 49434 if (!fsoSliderZoomedIn) { 49435 that.fsoSliderZoomIn(); 49436 fireEvent('userInteraction', 'zoom-in'); 49437 } else { 49438 that.fsoSliderZoomOut(); 49439 fireEvent('userInteraction', 'zoom-out'); 49440 } 49441 }); 49442 } 49443 49444 this.domParent.find('.slide-zoomer').on('click', function(e) { 49445 49446 if (!fsoSliderZoomedIn) { 49447 that.fsoSliderZoomIn(); 49448 fireEvent('userInteraction', 'zoom-in'); 49449 } else { 49450 that.fsoSliderZoomOut(); 49451 fireEvent('userInteraction', 'zoom-out'); 49452 } 49453 }); 49454 49455 49456 49457 49458 49459 49460 49461 49462 49463 this.domParent.find('.fso-slide-upper-bg .closebtn').on('click', function(e) { 49464 fireEvent('userInteraction', 'zoom-out'); 49465 that.fsoSliderZoomOut(); 49466 }); 49467 49468 $(window).on('scroll', function(e) { 49469 if (fsoSliderZoomedIn) that.fsoSliderZoomOut(); 49470 49471 }); 49472 49473 $(window).on('resize', function(e) { 49474 49475 if (fsoSliderZoomedIn) { 49476 49477 if (fsoSliderResizeTimer > 0) clearTimeout(fsoSliderResizeTimer);
49478 fsoSliderResizeTimer = setTimeout(function() { 49479 49480 $('.fso-slide-lower-bg .slide-selectors').css('left', $(window).width() / 2 - fsoSlideWidth / 2) 49481 var zoomRatio = that.fsoSliderCalculateZoomRatio(); 49482 $slideshowMainElem 49483 .css('-moz-transform', '-moz-translate3d(0px, ' + -zoomRatio.verticalDelta + 'px, 0px) scale3d(' + zoomRatio.scaleRatio + ',' + zoomRatio.scaleRatio + ', 1.0)') 49484 .css('-ms-transform', '-ms-translate3d(0px, ' + -zoomRatio.verticalDelta + 'px, 0px) scale3d(' + zoomRatio.scaleRatio + ',' + zoomRatio.scaleRatio + ', 1.0)') 49485 .css('-o-transform', '-o-translate(0px, ' + -zoomRatio.verticalDelta + 'px) scale3d(' + zoomRatio.scaleRatio + ',' + zoomRatio.scaleRatio + ', 1.0)') 49486 .css('transform', 'translate3d(0px, ' + -zoomRatio.verticalDelta + 'px, 0px) scale3d(' + zoomRatio.scaleRatio + ',' + zoomRatio.scaleRatio + ', 1.0)') 49487 .css('-webkit-transform', 'translate3d(0px, ' + -zoomRatio.verticalDelta + 'px, 0px) scale3d(' + zoomRatio.scaleRatio + ',' + zoomRatio.scaleRatio + ', 1.0)'); 49488 $('.additional-zoomer-elem') 49489 .css('-moz-transform', '-moz-translate3d(0px, ' + -zoomRatio.verticalDelta + 'px, 0px) scale3d(' + zoomRatio.scaleRatio + ',' + zoomRatio.scaleRatio + ', 1.0)') 49490 .css('-ms-transform', '-ms-translate3d(0px, ' + -zoomRatio.verticalDelta + 'px, 0px) scale3d(' + zoomRatio.scaleRatio + ',' + zoomRatio.scaleRatio + ', 1.0)') 49491 .css('-o-transform', '-o-translate(0px, ' + -zoomRatio.verticalDelta + 'px) scale3d(' + zoomRatio.scaleRatio + ',' + zoomRatio.scaleRatio + ', 1.0)') 49492 .css('transform', 'translate3d(0px, ' + -zoomRatio.verticalDelta + 'px, 0px) scale3d(' + zoomRatio.scaleRatio + ',' + zoomRatio.scaleRatio + ', 1.0)') 49493 .css('-webkit-transform', 'translate3d(0px, ' + -zoomRatio.verticalDelta + 'px, 0px) scale3d(' + zoomRatio.scaleRatio + ',' + zoomRatio.scaleRatio + ', 1.0)'); 49494 }, 300); 49495 } 49496 }); 49497 49498 } 49499 49500 49501 49502 if ((document.location.host.match(/hotline.org/) || document.location.host.match(/firechrome.org/)) && 49503 navigator.userAgent.match(/Autoscreencapper/) && document.location.hash.match(/slide/)) { 49504 $('#content_container,#wide_footer,#footer').css('display', 'none'); 49505 $('#header').css('overflow', 'hidden'); 49506 } 49507 49508 49509 49510 49511 49512 49513 49514 49515 49516 49517 49518 $(document).on('keydown', function(e) { 49519 49520 if ($('body').hasClass('editing-ip') || $('body').hasClass('editing') || $('body').hasClass('slide-captions-only')) return; 49521 49522 var d = e.srcElement || e.target; 49523 49524 49525 if ((d.tagName.toUpperCase() === 'INPUT' && (d.type.toUpperCase() === 'TEXT' || d.type.toUpperCase() === 'PASSWORD' || d.type.toUpperCase() === 'FILE')) || d.tagName.toUpperCase() === 'TEXTAREA') { 49526 return; 49527 } 49528 49529 49530 if ((e.which == 37 && !isRTL) || (e.which == 39 && isRTL)) { 49531 e.preventDefault(); 49532 sliderMovePrev(); 49533 fireEvent('userInteraction', 'key-prev'); 49534 } 49535 49536 else if ((e.which == 39 && !isRTL) || (e.which == 37 && isRTL)) { 49537 e.preventDefault(); 49538 that.sliderMoveNext(); 49539 fireEvent('userInteraction', 'key-next'); 49540 } 49541 49542 else if (that.zoomOnClick && e.which == 27) { 49543 e.preventDefault(); 49544 if (fsoSliderZoomedIn) that.fsoSliderZoomOut(); 49545 fireEvent('userInteraction', 'zoom-out'); 49546 } 49547 49548 else if (that.zoomOnClick && e.which == 32) { 49549 e.preventDefault(); 49550 if (fsoSliderZoomedIn) { 49551 that.fsoSliderZoomOut(); 49552 fireEvent('userInteraction', 'zoom-out'); 49553 } else { 49554 that.fsoSliderZoomIn(); 49555 fireEvent('userInteraction', 'zoom-in'); 49556 } 49557 } 49558 49559 }); 49560 49561 this.domParent.find('.slide-first-link').on('click', function(e) { 49562 49563 replaceHash('slide1'); 49564 fireEvent('userInteraction', 'click-first'); 49565 return false;
49566 }); 49567 49568 49569 49570 this.domParent.find('.slide-last-link').on('click', function(e) { 49571 49572 replaceHash('slide' + totalSlides); 49573 fireEvent('userInteraction', 'click-last'); 49574 return false; 49575 }); 49576 49577 49578 49579 49580 49581 49582 49583 49584 49585 if ($('html').hasClass('device-iphone')) { 49586 49587 49588 setTimeout(function() { 49589 49590 var videoStartTime, 49591 videoInitialX; 49592 49593 49594 49595 $('.vjs-poster').each(function() { 49596 var clone = this.cloneNode(true); 49597 this.parentNode.replaceChild(clone, this); 49598 }); 49599 49600 document.addEventListener('touchstart', function(e) { 49601 if (e && e.target && e.target.classList && e.target.classList.contains('vjs-poster')) { 49602 videoStartTime = (new Date()).getTime(); 49603 videoInitialX = e.changedTouches[0].pageX; 49604 } 49605 }, true); 49606 49607 document.addEventListener('touchend', function(e) { 49608 49609 if (e && e.target && e.target.classList && e.target.classList.contains('vjs-poster')) { 49610 49611 49612 49613 49614 if ( ((new Date()).getTime() - videoStartTime < 200) && 49615 (Math.abs(videoInitialX - e.changedTouches[0].pageX) < 20) ) { 49616 49617 e.preventDefault(); 49618 e.stopPropagation(); 49619 e.stopImmediatePropagation(); 49620 49621 var thisVideo = $(e.target).parents('slide').removeClass('paused') 49622 .addClass('playing').find('video')[0]; 49623 if (!thisVideo) return false; 49624 49625 thisVideo.play(); 49626 49627 49628 49629 if (!thisVideo.classList.contains('suspend-wired')) { 49630 thisVideo.addEventListener('suspend', function(e) { 49631 setTimeout(function() { 49632 $(thisVideo).parents('slide').addClass('paused'); 49633 }, 500); 49634 }); 49635 thisVideo.classList.add('suspend-wired'); 49636 } 49637 49638 return false; 49639 49640 } 49641 } 49642 }, true); 49643 49644 }, 3000); 49645 49646 } 49647 49648 49649 49650 49651 49652 if ($('html').hasClass('device-ipad')) { 49653 49654 new function() { 49655 49656 49657 49658 document.addEventListener('touchstart', function(e) { 49659 if (e && e.target && (e.target.tagName === 'VIDEO' || e.target.classList.contains('vjs-poster'))) { 49660 49661 e.target.onplay = function() { 49662 var all = document.getElementsByClassName('new-age-caption-overlay new-age-caption-overlay-reverse'); 49663 49664 for (var i = 0, max = all.length; i < max; i++) { 49665 all[i].style.visibility = 'hidden'; 49666 } 49667 }; 49668 49669 e.target.onpause = function() { 49670 var all = document.getElementsByClassName('new-age-caption-overlay new-age-caption-overlay-reverse'); 49671 49672 for (var i = 0, max = all.length; i < max; i++) { 49673 all[i].style.visibility = 'visible'; 49674 } 49675 }; 49676 } 49677 }, true); 49678 49679 49680 49681 49682 return; 49683 49684 var videoStartTime, 49685 videoInitialX; 49686 49687 document.addEventListener('touchstart', function(e) { 49688 if (e && e.target && (e.target.tagName === 'VIDEO' || e.target.classList.contains('vjs-poster'))) { 49689 videoStartTime = (new Date()).getTime(); 49690 videoInitialX = e.changedTouches[0].pageX; 49691 } 49692 }, true); 49693 49694 document.addEventListener('touchend', function(e) { 49695 49696 if (e && e.target && e.target.tagName === 'VIDEO') { 49697 49698 49699 49700 49701 if ( ((new Date()).getTime() - videoStartTime < 200) && 49702 (Math.abs(videoInitialX - e.changedTouches[0].pageX) < 20) ) { 49703 49704 e.target.controls = true; 49705 e.target.play(); 49706 e.target.parentNode.classList.add('ios-video-playing'); 49707 e.target.parentNode.parentNode.parentNode.classList.add('ios-video-playing'); 49708 49709 var thisVideo = e.target; 49710 e.target.addEventListener('pause', function(e) { 49711 thisVideo.controls = false;
49712 thisVideo.parentNode.classList.remove('ios-video-playing'); 49713 thisVideo.parentNode.parentNode.parentNode.classList.remove('ios-video-playing'); 49714 }); 49715 49716 } 49717 49718 } else { 49719 49720 if (e && e.target && e.target.classList.contains('vjs-poster')) { 49721 49722 if ( ((new Date()).getTime() - videoStartTime < 200) && 49723 (Math.abs(videoInitialX - e.changedTouches[0].pageX) < 20) ) { 49724 49725 var thisVideo = $(e.target).parent().find('video')[0]; 49726 49727 $(thisVideo).parent().addClass('ios-video-playing'); 49728 $(thisVideo).parent().parent().parent().addClass('ios-video-playing'); 49729 49730 thisVideo.addEventListener('pause', function(e) { 49731 thisVideo.controls = false; 49732 $(thisVideo).parent().removeClass('ios-video-playing'); 49733 $(thisVideo).parent().parent().parent().removeClass('ios-video-playing'); 49734 }); 49735 49736 } 49737 49738 } 49739 } 49740 49741 }, true); 49742 49743 }(); 49744 49745 } 49746 49747 49748 49749 49750 49751 $mySlides.find('.item,.inactive-dot').each(function() { 49752 49753 var $this = $(this); 49754 49755 49756 if ($this.attr('width')) { 49757 $this.css('width', $this.attr('width')); 49758 $this.removeAttr('width'); 49759 } 49760 49761 if ($this.attr('height')) { 49762 $this.css('height', $this.attr('height')); 49763 $this.removeAttr('height'); 49764 } 49765 49766 var myElem = $(this)[0]; 49767 for (var a in attrs) { 49768 var myVal = myElem.style[attrs[a]]; 49769 if (!myVal) continue; 49770 myVal = myVal.replace(/px/, ''); 49771 if (attrs[a] == 'width' && myVal == 0) continue; 49772 if (attrs[a] == 'height' && myVal == 0) continue; 49773 if (myVal == 'auto' || parseInt(myVal) == NaN) continue; 49774 $this.attr('data-' + attrs[a], parseInt(myVal)); 49775 } 49776 49777 }); 49778 49779 49780 49781 if (globalHas3d) 49782 $slideshowMainElem.css(Modernizr.prefixed('transform'), 'none'); 49783 49784 this.resize(); 49785 49786 if (window.deviceCustomType == 'desktop') { 49787 $(window).on('resize', function() { 49788 that.resize(); 49789 }); 49790 } 49791 49792 49793 $active = $('.fso-slide-active .video-js:not(.video-js-inited)'); 49794 if($active.length > 0) { 49795 initVideoJS($active[0]); 49796 } 49797 49798 setTimeout(function(){ 49799 49800 $('slide .video-js:not(.video-js-inited)').each(function() { 49801 initVideoJS(this); 49802 }); 49803 }, 3000); 49804 49805 49806 49807 $(window).on('orientationchange', function() { 49808 setTimeout( 49809 function() { 49810 that.resize(); 49811 }, 500); 49812 49813 }); 49814 49815 if (globalHas3d && Modernizr.csstransitions) 49816 $slideshowMainElem.css(Modernizr.prefixed('transition'), globalTransitionTransformProps[Modernizr.prefixed('transform')] + ' 1s cubic-bezier(0.165, 0.840, 0.440, 1.000)'); 49817 49818 $slideshowMainElem.css('opacity', 1); 49819 49820 var hammertime; 49821 49822 if (Modernizr.touch) { 49823 49824 if (window.deviceCustomType != 'desktop' && this.touchMode == 'photoswipe') { 49825 49826 loadJS('/theme/js/hammer-v2.0.4.min.js', function() { 49827 49828 49829 hammertime = new Hammer($mySlideshowWrapper[0]); 49830 49831 49832 hammertime.on('tap doubletap press swipe', function(e) { 49833 49834 49835 49836 49837 if (e.target && e.target.classList) { 49838 49839 if (e.target.classList.contains('menu-label') && !e.target.classList.contains('no-mobile-interaction')) { 49840 var $mySlideParent = $(e.target).parents('slide').addClass('show'); 49841 openLightbox($mySlideParent, '<div class="mobile-pac-menu"><ul></ul></div>'); 49842 $('.mobile-caption-lightbox-overlay').addClass('menu-lightbox'); 49843 $('.menu-bar-wrapper .menu-bar-item').each(function() { 49844 $('.mobile-pac-menu ul').append('<li class="mobile-menu-link" data-class="' + 49845 $(this).attr('data-class') + '">' + $(this).html() + '</li>'); 49846 }); 49847 49848 49849 e.preventDefault(); 49850 49851 49852 return false;
49853 } 49854 49855 if (e.target.classList.contains('mobile-menu-link')) { 49856 switch ($(e.target).attr('data-class')) { 49857 case 'menu-show': 49858 if (currentActiveSlideIdx != 8) { 49859 inTouch = false; 49860 fsoSliderGotoSlide(7, false); 49861 sliderPositionSlides(true); 49862 } 49863 break; 49864 case 'lad-show': 49865 if (currentActiveSlideIdx != 10) { 49866 inTouch = false; 49867 fsoSliderGotoSlide(9, false); 49868 sliderPositionSlides(true); 49869 } 49870 break; 49871 case 'hi-show': 49872 if (currentActiveSlideIdx != 24) { 49873 inTouch = false; 49874 fsoSliderGotoSlide(23, false); 49875 sliderPositionSlides(true); 49876 } 49877 break; 49878 case 'aola-show': 49879 if (currentActiveSlideIdx != 53) { 49880 inTouch = false; 49881 fsoSliderGotoSlide(52, false); 49882 sliderPositionSlides(true); 49883 } 49884 break; 49885 case 'asho-show': 49886 if (currentActiveSlideIdx != 34) { 49887 inTouch = false; 49888 fsoSliderGotoSlide(33, false); 49889 sliderPositionSlides(true); 49890 } 49891 break; 49892 } 49893 49894 49895 return; 49896 } 49897 49898 } 49899 49900 49901 49902 49903 if (!window.galleryItems) return console.error('we are too early'); 49904 if ($(e.target).hasClass('cancel-photoswipe-zoom') || $(e.target).parents('.cancel-photoswipe-zoom').length) return; 49905 49906 49907 49908 49909 49910 49911 49912 49913 49914 49915 49916 49917 49918 49919 window.gallery = new PhotoSwipe(document.querySelectorAll('.pswp')[0], PhotoSwipeUI_Default, galleryItems, { 49920 index: Math.max(0, currentActiveSlideIdx), 49921 history: false, 49922 }); 49923 49924 gallery.listen('afterChange', function() { 49925 49926 49927 49928 49929 currentActiveSlideIdx = gallery.getCurrentIndex(); 49930 49931 replaceHash('slide' + (currentActiveSlideIdx+1)); 49932 49933 var captionLength = $mySlides.eq(currentActiveSlideIdx).find('.text-item').text(); 49934 if (captionLength && captionLength.length > 500) { 49935 $('.pswp .pswp__caption__center').addClass('small-captions'); 49936 } else { 49937 $('.pswp .pswp__caption__center').removeClass('small-captions'); 49938 } 49939 49940 49941 49942 if($( document ).width() <= 768 && currentActiveSlideIdx != 0 && window.location.href != "/churches/bringing-scientology-world/scientology-media-productions.html" ) { 49943 $('.pswp__caption__center').html( $mySlides.eq(currentActiveSlideIdx).find('.text-item').html() ); 49944 $('.pswp__caption').removeClass('pswp__caption--empty'); 49945 $mySlides.eq(currentActiveSlideIdx).find('.text-item').addClass('pswp__caption--empty'); 49946 } 49947 49948 49949 if($( document ).width() <= 768 && currentActiveSlideIdx == 0 && $mySlides.eq(currentActiveSlideIdx).find('.single-video-slide-caption').length > 0 ) { 49950 $('.pswp__caption__center').html(''); 49951 } 49952 49953 }); 49954 49955 gallery.listen('close', function() { 49956 49957 $('.fso-slide-mobile-numbers').html($('.pswp__counter').text()); 49958 49959 $slideshowMainElem.addClass('force-show-spinner'); 49960 $slideshowMainElem.find('.slide-stretcher-outer').removeClass('invisible'); 49961 49962 49963 49964 currentActiveSlideIdx = gallery.getCurrentIndex(); 49965 $('slide.fso-slide-active').removeClass('fso-slide-active'); 49966 $mySlides.eq(currentActiveSlideIdx).addClass('fso-slide-active'); 49967 49968 49969 49970 $mySlides.eq(currentActiveSlideIdx).find('img[data-src]:not([src])').each(function() { 49971 var $this = $(this); 49972 var imgSrc = $this.attr('data-src'); 49973 49974 49975 if ($this.attr('data-height') && window.determineImageSources) imgSrc = determineImageSources(this)[0]; 49976 49977 $this.attr('src', imgSrc); 49978 }); 49979 49980 setTimeout(function() { 49981 $slideshowMainElem.removeClass('force-show-spinner'); 49982 }, 500); 49983 49984 49985 49986 49987 49988 hammertime.options.enable = true; 49989 49990 }); 49991 49992 49993 49994 49995 $slideshowMainElem.find('.slide-stretcher-outer').addClass('invisible'); 49996 49997 49998 hammertime.options.enable = false;
49999 50000 50001 gallery.init(); 50002 50003 50004 50005 50006 return false; 50007 50008 }); 50009 }); 50010 50011 } else { 50012 50013 $slideHolderDom.find('slide').each(function() { 50014 if ($(this).find('.text-item').length) { 50015 $(this).append('<div class="text-info-mobile glyphicon-info-sign"></div>'); 50016 } 50017 if ($(this).hasClass('slide-template-new-c-with-band') && $(this).find('.floater-texter.editable').length) { 50018 $(this).append('<div class="text-info-mobile glyphicon-info-sign"></div>'); 50019 } 50020 }); 50021 50022 slideshowMainElem.addEventListener('touchstart', function(e) { 50023 onTouchStart(e); 50024 fireEvent('userInteraction', 'touch-start'); 50025 return false; 50026 }, true); 50027 50028 slideshowMainElem.addEventListener('touchmove', function(e) { 50029 onTouchMove(e); 50030 return false; 50031 }, true); 50032 50033 slideshowMainElem.addEventListener('touchend', function(e) { 50034 onTouchEnd(e); 50035 return false; 50036 }, true); 50037 50038 slideshowMainElem.addEventListener('touchcancel', function(e) { 50039 onTouchEnd(e); 50040 return false; 50041 }, true); 50042 50043 50044 document.addEventListener('touchend', function(e) { 50045 50046 if (Math.abs(window.scrollY - slideshowScrollTop) < 20) { 50047 window.scrollTo(window.scrollX, slideshowScrollTop); 50048 } 50049 50050 50051 if (!e || !e.target || !e.target.classList) return; 50052 50053 if (isIpad) { 50054 50055 if (inTouch && $(e.target).parents('slide').length) { 50056 onTouchEnd(e); 50057 return; 50058 } 50059 50060 if (e.target.classList.contains('slide-arrow-holder') || 50061 $(e.target).parents('.slide-arrow-holder').length) { 50062 50063 var $me = $(e.target); 50064 if ($me.hasClass('slide-left-arrow2') || $me.parents('.slide-left-arrow2').length) { 50065 sliderMovePrev(); 50066 } else { 50067 that.sliderMoveNext(); 50068 } 50069 50070 e.preventDefault(); 50071 e.stopPropagation(); 50072 e.stopImmediatePropagation(); 50073 return false; 50074 } 50075 50076 if (e.target.classList.contains('menu-label') && !e.target.classList.contains('no-mobile-interaction')) { 50077 $slideshowMainElem.addClass('always-show'); 50078 e.preventDefault(); 50079 e.stopPropagation(); 50080 e.stopImmediatePropagation(); 50081 return; 50082 } 50083 50084 } 50085 50086 if (e.target.classList.contains('menu-label') && !e.target.classList.contains('no-mobile-interaction')) { 50087 var $mySlideParent = $(e.target).parents('slide').addClass('show'); 50088 openLightbox($mySlideParent, '<div class="mobile-pac-menu"><ul></ul></div>'); 50089 $('.mobile-caption-lightbox-overlay').addClass('menu-lightbox'); 50090 $('.fso-slide-imagemap.show-overlay-mobile-menu .active-dot.active-dot-slider').each(function() { 50091 $('.mobile-pac-menu ul').append('<li class="mobile-menu-link" data-class="' + 50092 $(this).attr('data-class') + '">' + $(this).html() + '</li>'); 50093 }); 50094 $('.mobile-pac-menu ul').append('<li class="mobile-menu-link" data-class="menu-show">Menu</li>'); 50095 50096 e.preventDefault(); 50097 e.stopPropagation(); 50098 e.stopImmediatePropagation(); 50099 return false;
50100 } 50101 50102 if (e.target.classList.contains('mobile-menu-link')) { 50103 closeLightbox($('slide.show').removeClass('show')); 50104 switch ($(e.target).attr('data-class')) { 50105 case 'menu-show': 50106 if (currentActiveSlideIdx != 8) { 50107 inTouch = false; 50108 fsoSliderGotoSlide(7, false); 50109 sliderPositionSlides(true); 50110 } 50111 break; 50112 case 'lad-show': 50113 if (currentActiveSlideIdx != 10) { 50114 inTouch = false; 50115 fsoSliderGotoSlide(9, false); 50116 sliderPositionSlides(true); 50117 } 50118 break; 50119 case 'hi-show': 50120 if (currentActiveSlideIdx != 24) { 50121 inTouch = false; 50122 fsoSliderGotoSlide(23, false); 50123 sliderPositionSlides(true); 50124 } 50125 break; 50126 case 'aola-show': 50127 if (currentActiveSlideIdx != 53) { 50128 inTouch = false; 50129 fsoSliderGotoSlide(52, false); 50130 sliderPositionSlides(true); 50131 } 50132 break; 50133 case 'asho-show': 50134 if (currentActiveSlideIdx != 34) { 50135 inTouch = false; 50136 fsoSliderGotoSlide(33, false); 50137 sliderPositionSlides(true); 50138 } 50139 break; 50140 } 50141 50142 50143 return; 50144 } 50145 50146 if (e.target.classList.contains('text-info-mobile')) { 50147 var $mySlideParent = $(e.target).parents('slide').addClass('show'); 50148 if (true || $mySlideParent.find('div.text-item').text().length > 200) { 50149 if ($mySlideParent.hasClass('slide-template-new-c-with-band') && $mySlideParent.find('.floater-texter.editable').length) { 50150 openLightbox($mySlideParent, $mySlideParent.find('.floater-texter.editable').html()); 50151 } else { 50152 openLightbox($mySlideParent, $mySlideParent.find('div.text-item').html()); 50153 } 50154 } else { 50155 $('body').addClass('mobile-text-info-showing'); 50156 } 50157 e.preventDefault(); 50158 e.stopPropagation(); 50159 e.stopImmediatePropagation(); 50160 return false; 50161 } 50162 50163 if (e.target.classList.contains('text-item') || 50164 e.target.classList.contains('mobile-caption-lightbox-overlay') || 50165 $(e.target).parents('.mobile-caption-lightbox-overlay').length) { 50166 closeLightbox($('slide.show').removeClass('show')); 50167 $('body').removeClass('mobile-text-info-showing'); 50168 e.preventDefault(); 50169 e.stopPropagation(); 50170 e.stopImmediatePropagation(); 50171 return false; 50172 } 50173 50174 }, true); 50175 50176 } 50177 50178 } 50179 50180 50181 50182 50183 50184 if (globalIsIOS) { 50185 $slideHolderDom.find('slide video').css('display', 'none'); 50186 $slideHolderDom.find('slide .vjs-poster').css('display', 'block'); 50187 } 50188 50189 logger.debug('responsive-slider.js end of init()'); 50190 50191 } 50192 50193 50194 50195 me.determineFirstSlide = function() { 50196 50197 50198 if (this.startSlide) return this.startSlide; 50199 50200 50201 if ((globalGetParams.video || globalGetParams['video-play']) && 50202 $slideshowMainElem.find('slide .video-js').length > 0) { 50203 50204 var myVideoParam = globalGetParams.video || globalGetParams['video-play']; 50205 50206 var slideMatching = _.reduce($slideshowMainElem.find('slide .video-js'), function(m, v) { 50207 if (m != null) return m; 50208 var mySlideId = $mySlides.index($(v).parents('slide')); 50209 50210 var cloned = $(v).parents('slide') && $(v).parents('slide')[0].className && $(v).parents('slide')[0].className.indexOf("clone") != -1; 50211 50212 if (getVideoIdForSlide(mySlideId) == myVideoParam && !cloned) return mySlideId; 50213 }, null); 50214 50215 50216 if (slideMatching !== void 0) { 50217 50218 if (this.autoplayVideos) startVideoForSlide(slideMatching); 50219 return parseInt(slideMatching) + 1; 50220 } 50221 50222 50223 50224 50225 if (globalGetParams.slide) { 50226 replaceVideoSource(globalGetParams.slide - 1, myVideoParam); 50227 if (this.autoplayVideos) startVideoForSlide(globalGetParams.slide - 1); 50228 return globalGetParams.slide; 50229 } 50230 50231 50232 replaceVideoSource(0, myVideoParam); 50233 return 1; 50234 50235 } 50236 50237 if (location.hash) { 50238 var newSlideIdx = parseInt(location.hash.substring(1).replace(/slide/, '')) - 1; 50239 if (!isNaN(newSlideIdx) && newSlideIdx >= 0 && newSlideIdx <= totalSlides - 1) 50240 return newSlideIdx + 1; 50241 } 50242 50243 50244 return 1; 50245 50246 } 50247 50248 50249 50250 50251 function getVideoIdForSlide(slideId) { 50252 var $myElem = $mySlides.eq(slideId).find('.video-js'); 50253 if (!$myElem) return null; 50254 var thisVideo = $myElem.attr('class') && $myElem.attr('class').match(/vjs-vi
50254deo-id-[^ ]*/); 50255 return thisVideo && thisVideo.length ? thisVideo[0].replace(/vjs-video-id-/, '') : null; 50256 } 50257 50258 function startVideoForSlide(slideId) { 50259 50260 50261 if (globalIsIOS) return; 50262 50263 logger.debug('startVideoForSlide, ' + slideId); 50264 50265 var $myVideoJsElem = $mySlides.eq(slideId).find('.video-js'); 50266 50267 if ($myVideoJsElem.length == 0) return; 50268 50269 50270 var videosPlayed = (cookieMonster.get('gsc_headers_videos_autoplayed') || ''); 50271 var videoId = getVideoIdForSlide(slideId); 50272 if (!globalGetParams['video-play'] && _.contains(videosPlayed.split(','), videoId)) return; 50273 50274 if (!(_.contains(videosPlayed.split(','), videoId))) 50275 videosPlayed = videosPlayed + (videosPlayed == "" ? "" : ",") + videoId; 50276 50277 50278 50279 50280 50281 fireEvent('videoAutoplay'); 50282 playVideoJS($myVideoJsElem); 50283 50284 } 50285 50286 function stopVideoForSlide(slideId) { 50287 50288 logger.debug('stopVideoForSlide, ' + slideId); 50289 50290 var $myVideoJsElem = $mySlides.eq(slideId).find('.video-js'); 50291 50292 if ($myVideoJsElem.length == 0) return; 50293 50294 var videoInst; 50295 if ($myVideoJsElem.prop('tagName') !== 'VIDEO') { 50296 videoInst = videojs($myVideoJsElem.get(0)); 50297 } 50298 50299 if (videoInst) videoInst.pause(); 50300 } 50301 50302 50303 function replaceVideoSource(slideId, videoId) { 50304 50305 if (!window.globalVideoSrc) return; 50306 50307 logger.debug('replaceVideoSource, ' + slideId + ', ' + videoId); 50308 50309 var $myVideoJsElem = $mySlides.eq(slideId).find('.video-js'); 50310 50311 var videoInst; 50312 if ($myVideoJsElem.prop('tagName') !== 'VIDEO') { 50313 var videoInst = videojs($mySlides.eq(slideId).find('.video-js').get(0)); 50314 } 50315 50316 var oldVideoId = getVideoIdForSlide(slideId); 50317 $mySlides.eq(slideId).find('.video-js').removeClass('vjs-video-id-' + oldVideoId) 50318 .addClass('vjs-video-id-' + videoId); 50319 50320 if (videoInst) { 50321 if (globalVideoSrc.match(/.*\.mp4.*/)) { 50322 videoInst.src({ type: 'video/mp4', src: globalVideoSrc }); 50323 } else { 50324 videoInst.src(globalVideoSrc); 50325 } 50326 } else { 50327 if (globalVideoSrc.match(/.*\.mp4.*/)) { 50328 $myVideoJsElem.find('source').eq(0).attr('src', globalVideoSrc).attr('type', 'video/mp4'); 50329 $myVideoJsElem.find('source:not([src="' + globalVideoSrc + '"])').remove(); 50330 } else { 50331 $myVideoJsElem.find('source').eq(0).attr('src', globalVideoSrc); 50332 } 50333 } 50334 50335 try { 50336 if (videoInst) { 50337 window.globalVideoThumb && videoInst.poster(globalVideoThumb); 50338 } else { 50339 window.globalVideoThumb && $myVideoJsElem.attr('poster', globalVideoThumb); 50340 } 50341 } catch (swallow) {} 50342 50343 if (videoInst) { 50344 $mySlides.eq(slideId).find('.vjs-poster').css('display', 'block'); 50345 } 50346 50347 return; 50348 } 50349 50350 50351 50352 50353 function openLightbox($aDomParent, aInnards) { 50354 $aDomParent.addClass('show-lightbox'); 50355 $('body').addClass('show-lightbox-caption').append('\ 50356 <div class="mobile-caption-lightbox-overlay">\ 50357 <table class="mobile-caption-lightbox-inner">\ 50358 <tr class="mobile-caption-lightbox-filler"></tr>\ 50359 <tr class="mobile-caption-lightbox"><td></td></tr>\ 50360 <tr class="mobile-caption-lightbox-filler"></tr>\ 50361 </table>\ 50362 </div>'); 50363 $('.mobile-caption-lightbox td').html(aInnards); 50364 } 50365 50366 function closeLightbox($aDomParent) { 50367 $aDomParent.removeClass('show-lightbox'); 50368 $('body').removeClass('show-lightbox-caption'); 50369 $('.mobile-caption-lightbox-overlay').remove(); 50370 } 50371 50372 50373 50374 50375 50376 50377 50378 50379 50380 50381 var initialX, 50382 50383 currentX, 50384 50385 startTime, 50386 50387 myMinSlideLeft, 50388 borkedTouch = false, 50389 myTextReset = true, 50390 inTouch = false; 50391 50392 50393 function canTouchThis(e) { 50394 return e.target.tagName == 'SLIDE' || $(e.target).parents('slide').length; 50395 } 50396 50397
50398 var start_touchx = null, 50399 start_touchy = null, 50400 max_touchx = 0, 50401 max_touchy = 0, 50402 locked_x = false, 50403 locked_y = false; 50404 50405 function onTouchStart(e) { 50406 50407 if (borkedTouch || !canTouchThis(e)) return; 50408 if (e.touches.length > 1) { 50409 borkedTouch = true; 50410 return onTouchEnd(e); 50411 } 50412 50413 50414 if (inTouch) globalAnimationController.cancelAnimation($('.slide-holder')); 50415 50416 50417 currentX = initialX = e.changedTouches[0].pageX; 50418 50419 startTime = (new Date()).getTime(); 50420 50421 borkedTouch = false; 50422 inTouch = true; 50423 50424 50425 50426 50427 50428 50429 sliderPositionSlides(true); 50430 50431 50432 50433 50434 50435 50436 start_touchx = e.touches[0].clientX; 50437 start_touchy = e.touches[0].clientY; 50438 max_touchx = 0; 50439 max_touchy = 0; 50440 locked_x = false; 50441 locked_y = false; 50442 50443 } 50444 50445 function onTouchMove(e) { 50446 50447 50448 50449 if (!inTouch || borkedTouch) return; 50450 50451 if (e.touches.length > 1) return onTouchEnd(e); 50452 50453 50454 50455 if (locked_y) return true; 50456 50457 max_touchx = Math.max(max_touchx, Math.abs(e.changedTouches[0].clientX - start_touchx)); 50458 max_touchy = Math.max(max_touchy, Math.abs(e.changedTouches[0].clientY - start_touchy)); 50459 50460 if (!locked_x && !locked_y && max_touchx > 12) { 50461 50462 locked_x = true; 50463 } 50464 50465 if (!locked_x && !locked_y && max_touchy > 12) { 50466 50467 locked_y = true; 50468 50469 50470 mySlideholderElem.style[myTransform] = 'translate3d(' + 50471 ((isRTL ? 1 : -1) * (parseInt(-slideholderDelta / 2) + currentActiveSlideIdx * fsoSlideWidth2 + 50472 (isCloned ? totalSlides * fsoSlideWidth2 : 0))) + 50473 'px,0px,0px)'; 50474 50475 return; 50476 } 50477 50478 var myX = e.changedTouches[0].pageX; 50479 50480 50481 50482 50483 50484 50485 50486 currentX = myX; 50487 50488 50489 50490 50491 50492 50493 50494 50495 50496 50497 var myLeft = myX - initialX; 50498 50499 50500 if (currentActiveSlideIdx == 0 && ((isRTL && myLeft < 10) || (!isRTL && myLeft > 10))) { 50501 myLeft -= myLeft * 0.7; 50502 } 50503 50504 if (currentActiveSlideIdx == totalSlides - 1 && ((isRTL && myLeft > 10) || (!isRTL && myLeft < 10))) { 50505 myLeft -= myLeft * 0.7; 50506 } 50507 50508 50509 50510 50511 mySlideholderElem.style[myTransform] = 'translate3d(' + 50512 (myLeft + (isRTL ? 1 : -1) * (parseInt(-slideholderDelta / 2) + currentActiveSlideIdx * fsoSlideWidth2 + 50513 (isCloned ? totalSlides * fsoSlideWidth2 : 0))) + 50514 'px,0px,0px)'; 50515 50516 50517 50518 50519 50520 50521 if (locked_x) e.preventDefault(); 50522 50523 } 50524 50525 function onTouchEnd(e) { 50526 50527 if (!inTouch) return; 50528 50529 if (e.touches.length == 0) borkedTouch = false; 50530 50531 if (e && e.target && e.target.classList && e.target.classList.contains && 50532 e.target.classList.contains('skip-touch-handling')) { 50533 return false; 50534 } 50535 50536 var myEndTime = (new Date()).getTime(); 50537 var myTotalTime = myEndTime - startTime; 50538 50539 50540 50541 50542 50543 50544 var myXPitch = currentX - initialX; 50545 50546 50547 var mySlideNum = currentActiveSlideIdx; 50548 50549 if (myXPitch > fsoSlideWidth2 / 2) { 50550 mySlideNum += (isRTL ? 1 : -1); 50551 } else if (myXPitch < -fsoSlideWidth2 / 2) { 50552 mySlideNum += (isRTL ? -1 : 1); 50553 } 50554 50555 50556 50557 50558 50559 50560 50561 50562 var isSwipe = false; 50563 if (myTotalTime < 400 && Math.abs(myXPitch) > 20) { 50564 isSwipe = true; 50565 if (myXPitch > 20) { 50566 mySlideNum = currentActiveSlideIdx + (isRTL ? 1 : -1); 50567 } else { 50568 mySlideNum = currentActiveSlideIdx + (isRTL ? -1 : 1); 50569 } 50570 } 50571 50572 50573 if (mySlideNum < 0) { 50574 mySlideNum = 0; 50575 } else if (mySlideNum >= totalSlides) { 50576 mySlideNum = totalSlides - 1; 50577 } 50578 50579 50580 50581 50582 myNewLeft = (isRTL ? 1 : -1) * (parseInt(-slideholderDelta / 2) + mySlideNum * fsoSlideWidth2); 50583 if (isCloned) myNewLeft += (isRTL ? 1 : -1) * totalSlides * fsoSlideWidth2; 50584 50585 50586 if (false && isSwipe) { 50587 if (currentActiveSlideIdx != mySlideNum) { 50588
50589 fsoSliderGotoSlide(mySlideNum, false); 50590 } 50591 sliderPositionSlides(true); 50592 } else { 50593 globalAnimationController.animate2d($('.slide-holder'), { 50594 x: myNewLeft, 50595 y: 0 50596 }, (isSwipe ? '.4s cubic-bezier(0.23, 1, 0.32, 1)' : '.5s cubic-bezier(0.23, 1, 0.32, 1)'), function() { 50597 if (currentActiveSlideIdx != mySlideNum) { 50598 50599 fsoSliderGotoSlide(mySlideNum, false); 50600 50601 $('.fso-slide-active').removeClass('fso-slide-active'); 50602 $mySlides.eq(mySlideNum).addClass('fso-slide-active'); 50603 } 50604 sliderPositionSlides(true); 50605 inTouch = false; 50606 }); 50607 } 50608 50609 } 50610 50611 50612 50613 50614 50615 me.resize = function() { 50616 50617 if (!this.hasLeftSidebar) { 50618 50619 fsoSlideWidth = this.slidesMax860 ? Math.min(860, $slideshowMainElem.width()) : $slideshowMainElem.width(); 50620 xRatio = fsoSlideWidth / 860; 50621 var fsoSlideWrapperWidth = $slideshowMainElem.width(); 50622 50623 50624 var windowHeight = $(window).height(); 50625 if (windowHeight < parseInt(this.fsoSliderZoomHeight * xRatio)) { 50626 xRatio = windowHeight / this.fsoSliderZoomHeight; 50627 fsoSlideWidth = parseInt(xRatio * 860); 50628 fsoSlideWrapperWidth = $(window).width(); 50629 } 50630 50631 fsoSlideWidth2 = $mySlides.eq(0).outerWidth(true); 50632 50633 slideholderDelta = fsoSlideWrapperWidth - fsoSlideWidth; 50634 50635 if (isIpad) slideholderDelta = 0; 50636 50637 } else { 50638 50639 50640 var currentWidth = $('#banner').width(); 50641 fsoSlideWidth = fsoSlideWidth2 = $mySlides.eq(0).outerWidth(true); 50642 xRatio = fsoSlideWidth / 860; 50643 slideholderDelta = 0; 50644 50645 } 50646 50647 slideshowScrollTop = slideshowMainElem.offsetTop; 50648 50649 50650 if (globalIsIE) return; 50651 50652 50653 50654 50655 if (!window.getCSSRule || /Trident/.test(navigator.userAgent)) { 50656 50657 50658 50659 var style = document.createElement('style'); 50660 style.appendChild(document.createTextNode('')); 50661 document.head.appendChild(style); 50662 var sheet = style.sheet; 50663 50664 sheet.insertRule('.slide-wrapper slide.slide-templated-new .text-item.animation-left-hold { ' + 50665 'opacity: 0 !important; ' + 50666 'transform: translate3d(' + ((isRTL ? 1 : -1) * fsoSlideWidth2) + 'px, 30px, 0px) !important; ' + 50667 '}', 0); 50668 50669 if (!window.cancelVerticalCaptionCentering && document.body.className.match(/templated-slider-build-2-1/)) { 50670 sheet.insertRule('.slide-wrapper slide.slide-templated-new .text-item.animation-left-hold.js-vertical-middle { ' + 50671 'opacity: 0 !important; ' + 50672 'transform: translate3d(' + ((isRTL ? 1 : -1) * fsoSlideWidth2) + 'px, -25%, 0px) !important; ' + 50673 '}', 1); 50674 } 50675 50676 sheet.insertRule('.slide-wrapper slide.slide-templated-new .text-item.animation-right-hold { ' + 50677 'opacity: 0 !important; ' + 50678 'transform: translate3d(' + ((isRTL ? -1 : 1) * fsoSlideWidth2) + 'px, 30px, 0px) !important; ' + 50679 '}', 2); 50680 if (!window.cancelVerticalCaptionCentering && document.body.className.match(/templated-slider-build-2-1/)) { 50681 sheet.insertRule('.slide-wrapper slide.slide-templated-new .text-item.animation-right-hold.js-vertical-middle { ' + 50682 'opacity: 0 !important; ' + 50683 'transform: translate3d(' + ((isRTL ? -1 : 1) * fsoSlideWidth2) + 'px, -25%, 0px) !important; ' + 50684 '}', 3); 50685 } 50686 50687 sheet.insertRule('.slide-wrapper slide.slide-templated-new .text-item.animation-left-hold2 { ' + 50688 'opacity: 1 !important; ' + 50689 'transform: translate3d(' + ((isRTL ? 1 : -1) * fsoSlideWidth2) + 'px, 0px, 0px) !important; ' + 50690 '}', 4); 50691 if (!window.cancelVerticalCaptionCentering && document.body.className.match(/templated-slider-build-2-1/)) { 50692 sheet.insertRule('.slide-wrapper slide.slide-templated-new .text-item.animation-left-hold2.js-vertical-middle { ' + 50693 'opacity: 1 !important; ' + 50694 'transform: translate3d(' + ((isRTL ? 1 : -1) * fsoSlideWidth2) + 'px, -50%, 0px) !important; ' + 50695 '}', 5); 50696 } 50697 50698 sheet.insertRule('.slide-wrapper slide.slide-templated-new .text-item.animation-right-hold2 { ' + 50699 'opacity: 1 !important; ' + 50700 'transform: translate3d(' + ((isRTL ? -1 : 1) * fsoSlideWidth2) + 'px, 0px, 0px) !important; ' + 50701 '}', 6); 50702 if (!window.cancelVerticalCaptionCentering && document.body.className.match(/templated-slider-build-2-1/)) { 50703 sheet.insertRule('.slide-wrapper slide.slide-templated-new .text-item.animation-right-hold2.js-vertical-middle { ' + 50704 'opacity: 1 !important; ' + 50705 'transform: translate3d(' + ((isRTL ? -1 : 1) * fsoSlideWidth2) + 'px, -50%, 0px) !important; ' + 50706 '}', 7); 50707 } 50708 50709 } else { 50710 if (!this.useSimpleSliding && !globalIsMobile && globalHas3d && Modernizr.csstransitions) { 50711 50712 var prefixedTransform = globalTransitionTransformProps[Modernizr.prefixed('transform')]; 50713 50714 getCSSRule('.slide-wrapper slide .item.text-item.animation-left-hold').style.cssText = 'opacity: 0 !important; ' + prefixedTransform + ': translate3d(' + ((isRTL ? 1 : -1) * fsoSlideWidth2) + 'px, 30px, 0px) !important;'; 50715 if (!window.cancelVerticalCaptionCentering && document.body.className.match(/templated-slider-build-2-1/) && getCSSRule('.slide-wrapper slide .item.text-item.animation-left-hold.js-vertical-middle')) getCSSRule('.slide-wrapper slide .item.text-item.animation-left-hold.js-vertical-middle').style.cssText = 'opacity: 0 !important; ' + prefixedTransform + ': translate3d(' + ((isRTL ? 1 : -1) * fsoSlideWidth2) + 'px, -25%, 0px) !important;'; 50716 50717 getCSSRule('.slide-wrapper slide .item.text-item.animation-right-hold').style.cssText = 'opacity: 0 !important; ' + prefixedTransform + ': translate3d(' + ((isRTL ? -1 : 1) * fsoSlideWidth2) + 'px, 30px, 0px) !important;'; 50718 if (!window.cancelVerticalCaptionCentering && document.body.className.match(/templated-slider-build-2-1/) && getCSSRule('.slide-wrapper slide .item.text-item.animation-right-hold.js-vertical-middle')) getCSSRule('.slide-wrapper slide .item.text-item.animation-right-hold.js-vertical-middle').style.cssText = 'opacity: 0 !important; ' + prefixedTransform + ': translate3d(' + ((isRTL ? -1 : 1) * fsoSlideWidth2) + 'px, -25%, 0px) !important;'; 50719 50720 getCSSRule('.slide-wrapper slide .item.text-item.animation-left-hold2').style.cssText = 'opacity: 1 !important; ' + prefixedTransform + ': translate3d(' + ((isRTL ? 1 : -1) * fsoSlideWidth2) + 'px, 0px, 0px) !important;'; 50721 if (!window.cancelVerticalCaptionCentering && document.body.className.match(/templated-slider-build-2-1/) && getCSSRule('.slide-wrapper slide .item.text-item.animation-left-hold2.js-vertical-middle')) getCSSRule('.slide-wrapper slide .item.text-item.animation-left-hold2.js-vertical-middle').style.cssText = 'opacity: 1 !important; ' + prefixedTransform + ': translate3d(' + ((isRTL ? 1 : -1) * fsoSlideWidth2) + 'px, -50%, 0px) !important;'; 50722 50723 getCSSRule('.slide-wrapper slide .item.text-item.animation-right-hold2').style.cssText = 'opacity: 1 !important; ' + prefixedTransform + ': translate3d(' + ((isRTL ? -1 : 1) * fsoSlideWidth2) + 'px, 0px, 0px) !important;'; 50724 if (!window.cancelVerticalCaptionCentering && document.body.className.match(/templated-slider-build-2-1/) && getCSSRule('.slide-wrapper slide .item.text-item.animation-right-hold2.js-vertical-middle')) getCSSRule('.slide-wrapper slide .item.text-item.animation-right-hold2.js-vertical-middle').style.cssText = 'opacity: 1 !important; ' + prefixedTransform + ': translate3d(' + ((isRTL ? -1 : 1) * fsoSlideWidth2) + 'px, -50%, 0px) !important;'; 50725 50726 } 50727 } 50728 50729
50730 fsoSliderRecalculateAppleDots(); 50731 fsoSliderFixAppleDots(currentActiveSlideIdx); 50732 50733 50734 $allNonImageElems.each(function() { 50735 resizeElem($(this)); 50736 }); 50737 50738 50739 $allImageElems.each(function() { 50740 if ($(this).hasClass('loaded')) { 50741 50742 resizeElem($(this)); 50743 } else { 50744 50745 50746 if (!/Trident/.test(navigator.userAgent)) { 50747 $(this).load(function() { 50748 resizeElem($(this)); 50749 }); 50750 } else { 50751 setTimeout(function() { 50752 resizeElem($(this)); 50753 }, 1000); 50754 setTimeout(function() { 50755 resizeElem($(this)); 50756 }, 2000); 50757 setTimeout(function() { 50758 resizeElem($(this)); 50759 }, 5000); 50760 } 50761 } 50762 }); 50763 50764 50765 $('.slide-arrow-holder .arrow-bg').width(parseInt(slideholderDelta / 2) + 50); 50766 if (slideholderDelta > 0) { 50767 $('.slide-arrow-holder .arrow-bg').addClass('gradiented'); 50768 50769 50770 } else { 50771 $('.slide-arrow-holder .arrow-bg').removeClass('gradiented'); 50772 50773 50774 } 50775 50776 50777 sliderPositionSlides(); 50778 50779 } 50780 50781 50782 function resizeElem($elem) { 50783 50784 if ($elem.hasClass('js-dynamic-size-set')) return; 50785 50786 for (var a in attrs) { 50787 var myAttrsName = attrs[a]; 50788 var myVal = $elem.attr('data-' + myAttrsName); 50789 if (myVal == null || myVal == 'NaN') continue; 50790 if ($elem.hasClass('dynamically-sized')) { 50791 myVal = Math.floor(myVal / xRatio); 50792 $elem.attr('data-' + myAttrsName, myVal); 50793 setTimeout(function() { 50794 $elem.removeClass('dynamically-sized'); 50795 }, 1000); 50796 } 50797 $elem.css(myAttrsName, Math.floor(myVal * xRatio)); 50798 } 50799 } 50800 50801 50802 50803 50804 50805 50806 function loadSurroundingSlides(aSlideNum) { 50807 50808 if (window.deviceCustomType != 'desktop') return; 50809 50810 logger.debug('loading surround: ' + aSlideNum); 50811 50812 var startNum = Math.min(Math.max(aSlideNum - 2, 0), totalSlides - 5); 50813 sliderReloadCounter = 0; 50814 $slideshowMainElem.removeClass('all-loaded'); 50815 for (var i = startNum; i < startNum + 5; i++) { 50816 var $thisSlide = $mySlides.eq(i); 50817 $thisSlide.find('.image-item[data-src]:not(.loaded),.image-holder img[data-src]:not(.loaded)').each(function() { 50818 sliderReloadCounter++; 50819 var $myThisSlide = $thisSlide; 50820 var $this = $(this); 50821 $this.css('background', 'none').css('visibility', 'hidden'); 50822 50823 if (!/Trident/.test(navigator.userAgent)) { 50824 50825 $this.load(function() { 50826 50827 $this.css('visibility', 'visible'); 50828 var $myMyThisSlide = $myThisSlide; 50829 50830 sliderLoaded(); 50831 }); 50832 50833 } 50834 50835 50836 50837 $(this).load(function() { 50838 $(this).removeClass('loading-image-hide'); 50839 $(this).parents('.image-holder').removeClass('js-preview'); 50840 }); 50841 50842 var imgSrc = $this.attr('data-src'); 50843 50844 50845 if ($this.attr('data-height') && window.determineImageSources) imgSrc = determineImageSources(this)[0]; 50846 50847 logger.debug('preloading surround: ' + imgSrc); 50848 $this.addClass('loading-image-hide').attr('src', imgSrc).addClass('loaded'); 50849 50850 if (/Trident/.test(navigator.userAgent)) { 50851 setTimeout(function() { 50852 50853 $this.css('visibility', 'visible'); 50854 var $myMyThisSlide = $myThisSlide; 50855 50856 sliderLoaded(); 50857 }, 3000); 50858 } 50859 50860 50861 if ($this.parents('.image-holder').length) { 50862 50863 var $double = $this.parents('.image-holder').find('.zoomer-image'); 50864 50865 (function() { 50866 setTimeout(function() { 50867 loadImage($double); 50868 }, (fsoSliderZoomedIn ? 0 : 500) + i * (fsoSliderZoomedIn ? 20 : 100)); 50869 })(); 50870 50871 } 50872 50873 50874 }); 50875 } 50876 if (sliderReloadCounter == 0) $slideshowMainElem.addClass('all-loaded'); 50877 50878 } 50879 50880 function sliderLoaded() { 50881 if (--sliderReloadCounter == 0) $slideshowMainElem.addClass('all-loaded'); 50882 } 50883 50884 50885 50886 function sliderMovePrev() { 50887 50888 if (currentActiveSlideIdx == 0 && !isCloned) return; 50889 50890 if (currentActiveSlideIdx > 0) { 50891 50892 return replaceHash('slide' + currentActiveSlideIdx); 50893 } 50894 50895 50896 globalAnimationController.cancelAnimation($slideHolderDom); 50897 50898 var deltaX = (isRTL ? -2 : 2) * totalSlides * 100; 50899 slideHolderDom.style[Modernizr.prefixed('transition')] = 'none'; 50900 slideHolderDom.offsetHeight; 50901 slideHolderDom.style[Modernizr.prefixed('transform')] = 'translate3d(-' + deltaX + '%, 0, 0)'; 50902 slideHolderDom.offsetHeight; 50903 50904 replaceHash('slide' + totalSlides); 50905 50906 } 50907 50908 me.sliderMovePrev = sliderMovePrev; 50909 50910 me.sliderMoveNext = function() { 50911 50912 if (currentActiveSlideIdx == totalSlides - 1 && !isCloned) return; 50913 50914 if (currentActiveSlideIdx < totalSlides - 1) { 50915 50916 return replaceHash('slide' + (currentActiveSlideIdx + 2)); 50917 } 50918 50919 50920 globalAnimationController.cancelAnimation($slideHolderDom); 50921 50922 var deltaX = (isRTL ? -1 : 1) * (totalSlides-1) * 100; 50923 slideHolderDom.style[Modernizr.prefixed('transition')] = 'none'; 50924 slideHolderDom.offsetHeight; 50925 slideHolderDom.style[Modernizr.prefixed('transform')] = 'translate3d(-' + deltaX + '%, 0, 0)'; 50926 slideHolderDom.offsetHeight; 50927 50928 replaceHash('slide1'); 50929 50930 } 50931 50932 50933 50934 50935 50936 function sliderEndAnimation(aForce) { 50937 50938 if (ipAnimation == 0 && !aForce) return; 50939 if (ipAnimation != 0) clearTimeout(ipAnimation); 50940 if (fsoSliderZoomTimeout > 0) clearTimeout(fsoSliderZoomTimeout); 50941 50942 $('.sliding .item').removeClass('move-left-fade-out move-right-fade-out move-left-fade-in move-right-fade-in move-left move-right text-left-effect text-right-effect'); 50943 50944 if (globalHas3d && Modernizr.csstransitions) 50945 $('.sliding .item').css(Modernizr.prefixed('transform'), 'none') 50946 .css(Modernizr.prefixed('transition'), 'none'); 50947 50948 if (globalHas3d) 50949 $('.sliding').css(Modernizr.prefixed('transform'), 'none'); 50950 50951 $('.sliding').removeClass('sliding'); 50952 50953 sliderPositionSlides(); 50954 50955 ipAnimation = 0; 50956 50957 } 50958 50959 function sliderPositionSlides(mobileOptimize) { 50960 50961 50962 50963 50964 if (Modernizr.csstransitions && mySlideholderElem.style[Modernizr.prefixed('transition')] != 'none') 50965 mySlideholderElem.style[Modernizr.prefixed('transition')] = 'none'; 50966 50967 50968 50969 var deltaX = (isRTL ? 1 : -1) * fsoSlideWidth2 * currentActiveSlideIdx; 50970 50971 50972 50973 50974 if (isCloned) { 50975 deltaX += (isRTL ? 1 : -1) * totalSlides * fsoSlideWidth2; 50976 } 50977 50978 50979 50980 50981 if (globalHas3d && Modernizr.csstransitions) { 50982 mySlideholderElem.style[Modernizr.prefixed('transform')] = 50983 'translate3d(' + deltaX + 'px,0,0)'; 50984 } else { 50985 if (window.globalGetParams && globalGetParams.headless) { 50986 mySlideholderElem.style[Modernizr.prefixed('transform')] = 50987 'translate3d(' + deltaX + 'px,0,0)'; 50988 } else { 50989 50990 mySlideholderElem.style.left = deltaX + 'px'; 50991 } 50992 } 50993 50994 if (!mobileOptimize) { 50995 50996 $('.slide-holder').removeClass('moving'); 50997 $slideshowMainElem.removeClass('moving'); 50998 $('.slide-holder').attr('class', $('.slide-holder').attr('class').replace(/[ ]*moving-off-of-[0-9]+/, '')); 50999 $slideshowMainElem.attr('class', $slideshowMainElem.attr('class').replace(/[ ]*moving-off-of-[0-9]+/, '')); 51000 $slideshowMainElem.attr('class', $slideshowMainElem.attr('class').replace(/[ ]*moving-onto-[0-9]+/, '')); 51001 $slideshowMainElem.attr('class', $slideshowMainElem.attr('class').replace(/[ ]*moving-to-dir-[^ ]+/, '')); 51002 51003 $('.fso-slide-active').removeClass('fso-slide-active'); 51004 if ($mySlide) { 51005 $mySlide.addClass('fso-slide-active'); 51006 } else { 51007 $mySlides.eq(currentActiveSlideIdx).addClass('fso-slide-active'); 51008 } 51009 51010 } 51011 51012 if (isIpad) $slideshowMainElem.removeClass('always-show'); 51013 51014 } 51015 51016 animationDebugMode = false;
51017 51018 function fsoSliderAnimateIn($mySlide, aExtraMotion, aDelay, aMoveinText) { 51019 51020 51021 if (globalIsIE) return aDelay; 51022 51023 if (aExtraMotion != 0) { 51024 51025 if (globalHas3d && Modernizr.csstransitions) { 51026 $mySlide.css(Modernizr.prefixed('transform'), 'translate3d(' + aExtraMotion + 'px, 0px, 0px)'); 51027 } else { 51028 $mySlide.css('left', -aExtraMotion + 'px'); 51029 } 51030 } 51031 51032 $mySlide.find('.item').each(function() { 51033 51034 var $myItem = $(this); 51035 var noAnimateDelay = $myItem.hasClass('no-animation-delay'); 51036 if ($myItem.hasClass('text-item')) { 51037 if (aMoveinText) { 51038 51039 var customDirection = myDirection; 51040 51041 51042 51043 51044 51045 51046 $myItem.css('opacity', 1).addClass('animation-' + customDirection + '-hold'); 51047 (function() { 51048 var theDirection = customDirection; 51049 textAnimationQueue.push(setTimeout(function() { 51050 $myItem.removeClass('animation-' + theDirection + '-hold').addClass('animation-' + theDirection + '-hold2'); 51051 }, 500 + aDelay)); 51052 })(); 51053 51054 51055 51056 51057 } 51058 } else { 51059 if (aMoveinText || noAnimateDelay) { 51060 var animateProps = { 51061 opacity: 1 51062 }; 51063 if (globalHas3d && Modernizr.csstransitions) { 51064 animateProps[Modernizr.prefixed('transform')] = 'translate3d(' + 51065 ((myDirection == 'left' ? minusOne : plusOne) * fsoSlideWidth2) + 51066 'px, 0px, 0px)'; 51067 globalAnimationController.animate( 51068 $myItem, 51069 animateProps, 51070 ( 51071 noAnimateDelay ? 51072 (animationDebugMode ? '10s' : '1s') + ' cubic-bezier(0.23, 1, 0.32, 1)' : 51073 (animationDebugMode ? '8s' : '.8s') + ' cubic-bezier(0.165, 0.840, 0.440, 1.000) ' + aDelay + 'ms' 51074 ) 51075 ); 51076 } else { 51077 $myItem.css('left', (myDirection == 'left' ? -1 : 1) * fsoSlideWidth2 + 'px'); 51078 } 51079 } else { 51080 $myItem.addClass('move-' + myDirection); 51081 } 51082 51083 51084 51085 51086 } 51087 aDelay += (animationDebugMode ? 500 : 50); 51088 51089 }); 51090 51091 return aDelay; 51092 51093 } 51094 51095 51096 function fsoSliderFixAppleDots(newSlideIdx) { 51097 51098 if (!tooManyAppleDots) return; 51099 51100 var animateProps = {}; 51101 51102 51103 if (newSlideIdx > totalSlides / 2) { 51104 51105 animateProps[isRTL ? 'right' : 'left'] = dotHolderWidth + ( 51106 fsoSliderZoomedIn ? 51107 ($('.slide-dot-over.slide-dot-over-left').eq(1).width() - lastSlideOffset) : 51108 ($('.slide-dot-over.slide-dot-over-left').eq(1).width() - lastSlideOffset) 51109 ); 51110 51111 $('.slide-dots-holder-inner').stop(true, true).animate(animateProps, 300, function() { 51112 $('.slide-link[href="#slide' + (midwaySlideNumLow + 5) + '"]').css('opacity', 0.8); 51113 $('.slide-link[href="#slide' + (midwaySlideNumLow + 4) + '"]').css('opacity', 0.4); 51114 $('.slide-link[href="#slide' + (midwaySlideNumLow + 3) + '"]').css('opacity', 0.2); 51115 $('.slide-link[href="#slide' + (midwaySlideNumLow + 2) + '"]').css('opacity', 0.1); 51116 $('.slide-link[href="#slide' + (midwaySlideNum - 1) + '"]').css('opacity', 1); 51117 $('.slide-link[href="#slide' + midwaySlideNum + '"]').css('opacity', 1); 51118 $('.slide-link[href="#slide' + (midwaySlideNum + 1) + '"]').css('opacity', 1); 51119 $('.slide-link[href="#slide' + (midwaySlideNum + 2) + '"]').css('opacity', 1); 51120 $('.slide-dots-holder-inner').css('opacity', 1); 51121 }); 51122 51123 } else { 51124 51125 animateProps[isRTL ? 'right' : 'left'] = $('.slide-dot-over.slide-dot-over-left').eq(1).width() + 2; 51126 51127 $('.slide-dots-holder-inner').stop(true, true).animate(animateProps, 300, function() { 51128 $('.slide-link[href="#slide' + (midwaySlideNumLow + 5) + '"]').css('opacity', 1); 51129 $('.slide-link[href="#slide' + (midwaySlideNumLow + 4) + '"]').css('opacity', 1); 51130 $('.slide-link[href="#slide' + (midwaySlideNumLow + 3) + '"]').css('opacity', 1); 51131 $('.slide-link[href="#slide' + (midwaySlideNumLow + 2) + '"]').css('opacity', 1); 51132 $('.slide-link[href="#slide' + (midwaySlideNum - 1) + '"]').css('opacity', 0.8); 51133 $('.slide-link[href="#slide' + midwaySlideNum + '"]').css('opacity', 0.4); 51134 $('.slide-link[href="#slide' + (midwaySlideNum + 1) + '"]').css('opacity', 0.2); 51135 $('.slide-link[href="#slide' + (midwaySlideNum + 2) + '"]').css('opacity', 0.1
51135); 51136 $('.slide-dots-holder-inner').css('opacity', 1); 51137 }); 51138 51139 } 51140 51141 } 51142 51143 51144 function fsoSliderRecalculateAppleDots() { 51145 51146 if (!tooManyAppleDots) return; 51147 51148 var currentDots = (fsoSliderZoomedIn ? allZoomedInAppleDots : allNormalAppleDots); 51149 51150 51151 if (!fsoSliderZoomedIn && $('.slide-dots-holder')[1]) { 51152 $('.slide-dots-holder')[1].offsetTop; 51153 dotHolderWidth = $('.slide-dots-holder').eq(1).width() - $('.slide-dot-over.slide-dot-over-left').eq(1).width() - $('.slide-dot-over.slide-dot-over-right').eq(1).width() + 30; 51154 } else { 51155 $('.slide-dots-holder')[0].offsetTop; 51156 dotHolderWidth = $('.slide-dots-holder').eq(0).width() - $('.slide-dot-over.slide-dot-over-left').eq(0).width() - $('.slide-dot-over.slide-dot-over-right').eq(0).width() + 30; 51157 } 51158 51159 if (dotHolderWidth >= lastSlideOffset) { 51160 $('body').addClass('fso-slides-wide-enough-for-apple-dots-no-slide'); 51161 } else { 51162 $('body').removeClass('fso-slides-wide-enough-for-apple-dots-no-slide'); 51163 } 51164 51165 midwaySlideNum = 0; 51166 midwaySlideNumLow = 0; 51167 51168 if (!isRTL) { 51169 for (var i = totalSlides; i >= 1; i--) { 51170 if (midwaySlideNum == 0 && currentDots.eq(i - 1).position().left < dotHolderWidth) { 51171 midwaySlideNum = i; 51172 } 51173 if (lastSlideOffset - currentDots.eq(i - 1).position().left < dotHolderWidth) { 51174 midwaySlideNumLow = Math.max(0, i - 3); 51175 } 51176 }; 51177 } else { 51178 var overallWidth = $('.slide-dots-holder-inner').eq(1).width(); 51179 for (var i = totalSlides; i >= 1; i--) { 51180 if (midwaySlideNum == 0 && overallWidth - currentDots.eq(i - 1).position().left < dotHolderWidth) { 51181 midwaySlideNum = i; 51182 } 51183 51184 if (lastSlideOffset - (overallWidth - currentDots.eq(i - 1).position().left) < dotHolderWidth) { 51185 midwaySlideNumLow = Math.max(0, i - 3); 51186 } 51187 }; 51188 } 51189 51190 $('.slide-link').css('opacity', 1); 51191 51192 } 51193 51194 function fsoSliderHashChange() { 51195 51196 var newSlideIdx = parseInt(location.hash.substring(1).replace(/slide/, '')) - 1; 51197 if (isNaN(newSlideIdx) || newSlideIdx < 0 || newSlideIdx > totalSlides - 1) return; 51198 51199 if (currentActiveSlideIdx == newSlideIdx) return; 51200 51201 if (!isIpad) { 51202 fsoSliderGotoSlide(newSlideIdx, true); 51203 } else { 51204 fsoSliderGotoSlide(newSlideIdx, false); 51205 sliderPositionSlides(true); 51206 } 51207 51208 } 51209 51210 function fsoSliderGotoSlide(newSlideIdx, animate) { 51211 51212 if (!window.slide8Ignore) { 51213 51214 if (newSlideIdx > 6) { 51215 $('.menu-holder-after-8').fadeIn(300); 51216 } else { 51217 $('.menu-holder-after-8').hide(); 51218 } 51219 51220 if (newSlideIdx == 7) { 51221 $('.fso-custom-menu').addClass('always-show'); 51222 $('body').addClass('hide-zoomer'); 51223 } else { 51224 $('.fso-custom-menu').removeClass('always-show'); 51225 $('body').removeClass('hide-zoomer'); 51226 } 51227 51228 } 51229 51230 51231 $('.slide-link').removeClass('selected');
51232 $('.slide-link[href="#slide' + (newSlideIdx + 1) + '"]').addClass('selected'); 51233 51234 51235 51236 if (animate) fsoSliderFixAppleDots(newSlideIdx); 51237 51238 setTimeout(function() { 51239 loadSurroundingSlides(newSlideIdx); 51240 }, 100); 51241 51242 51243 51244 51245 if (animate) { 51246 51247 if (!simpleAnimation) { 51248 51249 sliderEndAnimation(); 51250 51251 51252 for (var i = 0; i < textAnimationQueue.length; i++) { 51253 clearTimeout(textAnimationQueue[i]); 51254 } 51255 $('.text-item').removeClass('animation-left-hold animation-left-hold2 animation-right-hold animation-right-hold2'); 51256 51257 } 51258 51259 51260 myDirection = (currentActiveSlideIdx < newSlideIdx) ? 'left' : 'right'; 51261 var myDelay = 0; 51262 var $myActiveSlide = $mySlides.eq(currentActiveSlideIdx).addClass('sliding'); 51263 $mySlide = $mySlides.eq(newSlideIdx).addClass('sliding'); 51264 51265 51266 var $myNextSlide = null; 51267 var $myPrevSlide = null; 51268 if (myDirection === 'left') { 51269 if (newSlideIdx < totalSlides - 1) 51270 $myNextSlide = $mySlides.eq(newSlideIdx + 1).addClass('sliding'); 51271 if (currentActiveSlideIdx > 0) 51272 $myPrevSlide = $mySlides.eq(currentActiveSlideIdx - 1).addClass('sliding'); 51273 } else { 51274 if (newSlideIdx > 0) 51275 $myNextSlide = $mySlides.eq(newSlideIdx - 1).addClass('sliding'); 51276 if (currentActiveSlideIdx < totalSlides - 1) 51277 $myPrevSlide = $mySlides.eq(currentActiveSlideIdx + 1).addClass('sliding'); 51278 } 51279 51280 51281 51282 $('.slide-holder').addClass('moving'); 51283 $slideshowMainElem.addClass('moving'); 51284 $('.slide-holder').addClass('moving-off-of-' + currentActiveSlideIdx); 51285 $slideshowMainElem.addClass('moving-off-of-' + currentActiveSlideIdx); 51286 $slideshowMainElem.addClass('moving-onto-' + (newSlideIdx + 1)); 51287 $slideshowMainElem.addClass('moving-to-dir-' + myDirection); 51288 51289 if (!simpleAnimation) { 51290 51291 51292 var extraMotion = 0; 51293 var nextExtraMotion = 0; 51294 if (Math.abs(currentActiveSlideIdx - newSlideIdx) > 1) { 51295 extraMotion = (currentActiveSlideIdx - newSlideIdx) * (fsoSlideWidth2); 51296 extraMotion += (fsoSlideWidth2) * ((currentActiveSlideIdx < newSlideIdx) ? +1 : -1); 51297 nextExtraMotion = extraMotion + (fsoSlideWidth2) * ((currentActiveSlideIdx < newSlideIdx) ? +1 : -1); 51298 } 51299 51300 if ($myPrevSlide) { 51301 $myPrevSlide.find('.item').each(function() { 51302 51303 var noAnimateDelay = $(this).hasClass('no-animation-delay'); 51304 51305 if (globalHas3d && Modernizr.csstransitions) { 51306 var animateProps = { 51307 opacity: window.globalSlideshowFadeout !== void 0 ? globalSlideshowFadeout : 0.2, 51308 }; 51309 if ($(this).hasClass('text-item')) animateProps.opacity = 0; 51310 animateProps[Modernizr.prefixed('transform')] = 'translate3d(' + 51311 ((myDirection == 'left' ? minusOne : plusOne) * fsoSlideWidth2) + 51312 'px, 0px, 0px)'; 51313 globalAnimationController.animate( 51314 $(this), 51315 animateProps, 51316 ( 51317 noAnimateDelay ? 51318 (animationDebugMode ? '10s' : '1s') + ' cubic-bezier(0.23, 1, 0.32, 1)' : 51319 (animationDebugMode ? '6s' : '.6s') + ' cubic-bezier(0.165, 0.840, 0.440, 1.000)' 51320 ) 51321 ); 51322 } else { 51323 $(this).css('left', (myDirection == 'left' ? -1 : 1) * fsoSlideWidth2 + 'px'); 51324 } 51325 51326 51327 51328 }); 51329 } 51330 51331 $myActiveSlide.find('.item').each(function() { 51332 var $myItem = $(this); 51333 var noAnimateDelay = $myItem.hasClass('no-animation-delay'); 51334 51335 if (globalHas3d && Modernizr.csstransitions) { 51336 var animateProps = { 51337 opacity: window.globalSlideshowFadeout !== void 0 ? globalSlideshowFadeout : 0.2, 51338 }; 51339 if ($(this).hasClass('text-item')) animateProps.opacity = 0; 51340 animateProps[Modernizr.prefixed('transform')] = 'translate3d(' + 51341 ((myDirection == 'left' ? minusOne : plusOne) * fsoSlideWidth2) + 51342 'px, 0px, 0px)'; 51343 globalAnimationController.animate( 51344 $(this), 51345 animateProps, 51346 ( 51347 noAnimateDelay ? 51348 (animationDebugMode ? '10s' : '1s') + ' cubic-bezier(0.23, 1, 0.32, 1)' : 51349 (animationDebugMode ? '6s' : '.6s') + ' cubic-bezier(0.165, 0.840, 0.440, 1.000) ' + myDelay + 'ms' 51350 ) 51351 ); 51352 } else {
51353 $(this).css('left', (myDirection == 'left' ? -1 : 1) * fsoSlideWidth2 + 'px'); 51354 } 51355 51356 51357 51358 51359 myDelay += (animationDebugMode ? 500 : 50); 51360 }); 51361 51362 myDelay = Math.max(0, myDelay - (animationDebugMode ? 500 : 50)); 51363 51364 if (isCloned) { 51365 nextExtraMotion += (isRTL ? 1 : -1) * totalSlides * fsoSlideWidth2; 51366 extraMotion += (isRTL ? 1 : -1) * totalSlides * fsoSlideWidth2; 51367 } 51368 51369 } 51370 51371 if (!simpleAnimation) { 51372 if ($myNextSlide) fsoSliderAnimateIn($myNextSlide, plusOne * nextExtraMotion, 0, false); 51373 myDelay = fsoSliderAnimateIn($mySlide, plusOne * (isRTL ? -1 : 1) * extraMotion, myDelay, true); 51374 51375 ipAnimation = setTimeout(function() { 51376 sliderEndAnimation(); 51377 }, myDelay + animationGracePeriod); 51378 51379 51380 51381 51382 } else { 51383 51384 51385 51386 var deltaX = (isRTL ? -1 : 1) * newSlideIdx * 100; 51387 51388 if (isCloned) { 51389 deltaX += (isRTL ? -1 : 1) * totalSlides * 100; 51390 } 51391 51392 var animateProps = {}; 51393 animateProps[Modernizr.prefixed('transform')] = 'translate3d(' + (-deltaX) + '%, 0, 0)'; 51394 51395 globalAnimationController.animate( 51396 $slideHolderDom, 51397 animateProps, 51398 (animationDebugMode ? '8s' : '.8s') + ' cubic-bezier(0.165, 0.840, 0.440, 1.000)', 51399 function() { 51400 sliderEndAnimation(true); 51401 } 51402 ); 51403 51404 51405 51406 $('.fso-slide-active').removeClass('fso-slide-active'); 51407 $mySlides.eq(newSlideIdx).addClass('fso-slide-active'); 51408 51409 } 51410 51411 } else { 51412 $mySlide = $mySlides.eq(newSlideIdx); 51413 } 51414 51415 51416 me.slideWillChange(); 51417 51418 51419 currentActiveSlideIdx = newSlideIdx; 51420 $slideshowMainElem.attr('data-slide-active', newSlideIdx); 51421 $('.fso-slide-mobile-numbers .slide-num').text(currentActiveSlideIdx + 1); 51422 51423 if ((newSlideIdx == totalSlides - 1)) { 51424 $slideshowMainElem.addClass('fso-slide-last'); 51425 $('.slideshow-holder').addClass('fso-slider-last'); 51426 } else { 51427 $slideshowMainElem.removeClass('fso-slide-last'); 51428 $('.slideshow-holder').removeClass('fso-slider-last'); 51429 } 51430 51431 if (newSlideIdx == 0) { 51432 $('.slideshow-holder').addClass('slider-first-slide'); 51433 } else { 51434 $('.slideshow-holder').removeClass('slider-first-slide'); 51435 } 51436 51437 51438 me.slideHasChanged(); 51439 51440 }; 51441 51442 me.slideWillChange = function() { 51443 if (!globalIsIOS) { 51444 if ($mySlides.eq(currentActiveSlideIdx).find('.video-js').length) stopVideoForSlide(currentActiveSlideIdx); 51445 } 51446 $slideshowMainElem.trigger('slide-will-change', currentActiveSlideIdx); 51447 } 51448 51449 me.slideHasChanged = function() { 51450 51451 if($('.slide-number').length > 0) { 51452 $('.slide-number current').text(currentActiveSlideIdx+1); 51453 $('.slide-number total').text(this.totalSlides()); 51454 } 51455 51456 51457 var $customSlide = $('.custom-slide-leftnav[data-slide-id="' + $mySlides.eq(currentActiveSlideIdx).attr('data-slide-id') + '"]'); 51458 $('.custom-slide-leftnav.show').removeClass('show'); 51459 51460 if ($customSlide.length) { 51461 $('.slideshow-left-nav').addClass('custom-slide-showing'); 51462 $customSlide.addClass('show'); 51463 } else { 51464 $('.slideshow-left-nav').removeClass('custom-slide-showing'); 51465 } 51466 51467 51468 var slideArrowRight = $slideshowMainElem.find('.slide-right-arrow2')[0]; 51469 51470 if(!this.isCircularClone() && slideArrowLeft != undefined && slideArrowRight != undefined) { 51471 if(currentActiveSlideIdx + 1 == this.totalSlides()) { 51472 $(slideArrowRight).hide(); 51473 } else { 51474 $(slideArrowRight).show(); 51475 } 51476 51477 if(currentActiveSlideIdx == 0) { 51478 $(slideArrowLeft).hide(); 51479 } else { 51480 $(slideArrowLeft).show(); 51481 } 51482 } 51483 51484 $slideshowMainElem.trigger('slide-has-changed', currentActiveSlideIdx); 51485 51486 } 51487 51488 me.fsoSliderZoomIn = function() { 51489 51490 if(window.location.pathname == '/scientology-today/events/scientology-network.html') { 51491 return; 51492 } 51493 51494 51495 if (globalIsIE) return; 51496 51497 51498 if (screen && screen.width && screen.width <= 800) return; 51499 51500 if (fsoSliderZoomedIn) return; 51501 sliderEndAnimation(); 51502 51503 $('body')[0].scrollTop = 0; 51504 51505 $('.fso-slide-lower-bg .slide-selectors').css('left', $(window).width() / 2 - fsoSlideWidth / 2); 51506 51507 if (fsoSliderZoomTimeout > 0) clearTimeout(fsoSliderZoomTimeout); 51508 51509 $('.fso-zoomer').addClass('fadeOut').removeClass('fadeIn'); 51510 51511 51512 51513 $('.fso-custom-slider-fader').css('display', 'block'); 51514 51515 51516 51517 51518 var zoomRatio = this.fsoSliderCalculateZoomRatio(); 51519 51520 $slideshowMainElem 51521 .css('-moz-transform', '-moz-translate3d(0px, ' + -zoomRatio.verticalDelta + 'px, 0px) scale3d(' + zoomRatio.scaleRatio + ',' + zoomRatio.scaleRatio + ',1.0)') 51522 .css('-ms-transform', '-ms-translate3d(0px, ' + -zoomRatio.verticalDelta + 'px, 0px) scale3d(' + zoomRatio.scaleRatio + ',' + zoomRatio.scaleRatio + ',1.0)') 51523 .css('-o-transform', '-o-translate(0px, ' + -zoomRatio.verticalDelta + 'px) scale3d(' + zoomRatio.scaleRatio + ',' + zoomRatio.scaleRatio + ',1.0)') 51524 .css('transform', 'translate3d(0px, ' + -zoomRatio.verticalDelta + 'px, 0px) scale3d(' + zoomRatio.scaleRatio + ',' + zoomRatio.scaleRatio + ',1.0)') 51525 .css('-webkit-transform', 'translate3d(0px, ' + -zoomRatio.verticalDelta + 'px, 0px) scale3d(' + zoomRatio.scaleRatio + ',' + zoomRatio.scaleRatio + ',1.0)'); 51526 $('.additional-zoomer-elem') 51527 .css('-moz-transform', '-moz-translate3d(0px, ' + -zoomRatio.verticalDelta + 'px, 0px) scale3d(' + zoomRatio.scaleRatio + ',' + zoomRatio.scaleRatio + ',1.0)') 51528 .css('-ms-transform', '-ms-translate3d(0px, ' + -zoomRatio.verticalDelta + 'px, 0px) scale3d(' + zoomRatio.scaleRatio + ',' + zoomRatio.scaleRatio + ',1.0)') 51529 .css('-o-transform', '-o-translate(0px, ' + -zoomRatio.verticalDelta + 'px) scale3d(' + zoomRatio.scaleRatio + ',' + zoomRatio.scaleRatio + ',1.0)') 51530 .css('transform', 'translate3d(0px, ' + -zoomRatio.verticalDelta + 'px, 0px) scale3d(' + zoomRatio.scaleRatio + ',' + zoomRatio.scaleRatio + ',1.0)') 51531 .css('-webkit-transform', 'translate3d(0px, ' + -zoomRatio.verticalDelta + 'px, 0px) scale3d(' + zoomRatio.scaleRatio + ',' + zoomRatio.scaleRatio + ',1.0)'); 51532 51533 $slideshowMainElem.addClass('zoomedIn'); 51534 $('body').addClass('slideshow-zoomed-in'); 51535 51536 51537 51538 setTimeout(function() {
51539 fsoSliderZoomedIn = true; 51540 }, 50); 51541 51542 51543 51544 51545 51546 $('.slide-dots-holder-inner').css('opacity', 0); 51547 setTimeout(function() { 51548 fsoSliderRecalculateAppleDots(); 51549 fsoSliderFixAppleDots(currentActiveSlideIdx); 51550 }, 1000); 51551 51552 51553 $mySlides.eq(currentActiveSlideIdx).find('.image-holder .zoomer-image:not([src]):not(.loaded)').each(function() { 51554 loadImage($(this)); 51555 }); 51556 51557 51558 var i = 0; 51559 $mySlides.find('.image-holder .zoomer-image:not([src]):not(.loaded)').each(function() { 51560 var $this = $(this); 51561 (function() { 51562 setTimeout(function() { 51563 loadImage($this); 51564 }, 1000 * i++); 51565 })(); 51566 }); 51567 51568 } 51569 51570 51571 function loadImage($this) { 51572 51573 if (!$this[0]) return; 51574 51575 if (!$this.hasClass('loaded') && !$this.attr('src')) { 51576 logger.debug('(pre)loading double: ' + $this.attr('data-double-src')); 51577 $this.load(function() { 51578 $this.addClass('loaded').parent('.image-holder').addClass('double-loaded'); 51579 }); 51580 $this.attr('src', $this.attr('data-double-src')); 51581 } 51582 } 51583 51584 me.fsoSliderZoomOut = function() { 51585 51586 51587 if (globalIsIE) return; 51588 51589 if (!fsoSliderZoomedIn) return; 51590 sliderEndAnimation(); 51591 51592 $('.fso-zoomer').css('display', 'block').addClass('fadeIn').removeClass('fadeOut'); 51593 51594 51595 $('.fso-custom-slider-fader').css('display', 'none'); 51596 51597 51598 51599 fsoSliderZoomTimeout = setTimeout(function() { 51600 51601 fsoSliderZoomTimeout = 0; 51602 }, 1500); 51603 51604 $slideshowMainElem.css('transform', 'none') 51605 .css('-moz-transform', 'none') 51606 .css('-ms-transform', 'none') 51607 .css('-o-transform', 'none') 51608 .css('-webkit-transform', 'none'); 51609 $('.additional-zoomer-elem').css('transform', 'none') 51610 .css('-moz-transform', 'none') 51611 .css('-ms-transform', 'none') 51612 .css('-o-transform', 'none') 51613 .css('-webkit-transform', 'none'); 51614 51615 $slideshowMainElem.removeClass('zoomedIn'); 51616 $('body').removeClass('slideshow-zoomed-in'); 51617 fsoSliderZoomedIn = false; 51618 51619 51620 51621 51622 51623 $('.slide-dots-holder-inner').css('opacity', 0); 51624 setTimeout(function() { 51625 fsoSliderRecalculateAppleDots(); 51626 fsoSliderFixAppleDots(currentActiveSlideIdx); 51627 }, 1000); 51628 51629 } 51630 51631 me.fsoSliderCalculateZoomRatio = function() { 51632 51633 var tempSliderTop = fsoSliderTop || 0; 51634 51635 51636 tempSliderTop = 0; 51637 51638 var windowWidth = $(window).width(); 51639 if (!fsoSliderZoomedIn) windowWidth += 15; 51640 51641 var horizRatio = (windowWidth - 200) / fsoSlideWidth; 51642 var verticalRatio = ($(window).height() - 120) / this.fsoSliderZoomHeight; 51643 var scaleRatio = Math.min(horizRatio, verticalRatio); 51644 51645 if (scaleRatio < 1) scaleRatio = 1; 51646 51647 var myNewTop = Math.floor(tempSliderTop - ((this.fsoSliderZoomHeight / 2) * scaleRatio - (this.fsoSliderZoomHeight / 2))); 51648 51649 51650 if (scaleRatio == horizRatio) { 51651 myNewTop -= Math.floor(($(window).height() - this.fsoSliderZoomHeight * scaleRatio) / 2); 51652 } else { 51653 myNewTop -= (this.sliderZoomTopAdjustment ? this.sliderZoomTopAdjustment : 0) + 40; 51654 } 51655 51656 myNewTop -= $(window).scrollTop(); 51657 if ($('#header_and_content_container').length) { 51658 myNewTop += $('#header_and_content_container').position().top; 51659 } else if ($('#header_and_content').length) { 51660 myNewTop += $('#header_and_content').position().top; 51661 } 51662 51663 51664 if (window.deviceCustomType && deviceCustomType == 'desktop') { 51665 scaleRatio = 1; 51666 console.log(this.fsoSliderZoomHeight); 51667 console.log(tempSliderTop); 51668 51669 myNewTop = Math.floor(($(window).height() - this.fsoSliderZoomHeight * scaleRatio) / 2); 51670 myNewTop -= $('#header_and_content').position().top; 51671 myNewTop *= -1; 51672 console.log(myNewTop); 51673 } 51674 51675 return { 51676 verticalDelta: myNewTop, 51677 scaleRatio: scaleRatio 51678 }; 51679 51680 } 51681 51682 51683 me.currentSlideIdx = function() { 51684 return currentActiveSlideIdx; 51685 } 51686 51687 51688 me.totalSlides = function() { 51689 return totalSlides; 51690 } 51691 51692 51693 me.isCircularClone = function() { 51694 return isCloned; 51695 } 51696 51697 51698 function fireEvent(eventName, eventData) { 51699 $mySlideshowWrapper.trigger(eventName, eventData); 51700 } 51701 51702 51703 function replaceHash(newHash) { 51704 if (history && history.replaceState) { 51705 history.replaceState({}, document.title, '#' + newHash); 51706 return fsoSliderHashChange(); 51707 } 51708 location.hash = newHash; 51709 } 51710 51711 me.startVideoForSlide = function(num) { 51712 $myVideoJsElem = $mySlides.eq(num).addClass('ios-video-playing').find('.video-js'); 51713 if ($myVideoJsElem.length) { 51714 playVideoJS($myVideoJsElem); 51715 } 51716 } 51717
51718 me.gotoSlide = function(num) { 51719 if(num != currentActiveSlideIdx) 51720 fsoSliderGotoSlide(num, true); 51721 } 51722 51723 me.replaceHash = function(num) { 51724 replaceHash(num); 51725 } 51726 51727 function initVideoJS(el) { 51728 51729 51730 51731 if ($('html').hasClass('device-ipad')) { 51732 51733 $('video').each(function() { 51734 this.preload = 'auto'; 51735 this.controls = true; 51736 var thisVideo = this; 51737 this.addEventListener('play', function(e) { 51738 thisVideo.parentNode.classList.add('ios-video-playing'); 51739 51740 }); 51741 }); 51742 return { 51743 play: function() {} 51744 } 51745 } 51746 51747 el = $(el); 51748 el.addClass('video-js-inited') 51749 var vid; 51750 if (el && el.get(0) && el.get(0).tagName && el.get(0).tagName.toUpperCase() === 'VIDEO') { 51751 var opts; 51752 try { 51753 opts = JSON.parse(el.attr('data-gsc-setup')); 51754 } catch (e) {} 51755 51756 if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1 && 51757 navigator.platform.indexOf("Win") > -1 && 51758 el.find('source').eq(0).attr('type') != 'video/webm') { 51759 opts.techOrder = ["hls", "html5", "flash"]; 51760 } 51761 51762 opts.html5 = { 51763 nativeVideoTracks : false, 51764 nativeAudioTracks : false, 51765 hls: { 51766 overrideNative: true, 51767 } 51768 }; 51769 51770 opts.techOrder = ['hls', 'html5', 'flash']; 51771 51772 vid = videojs(el.get(0), opts || 51773 { 51774 controls: true, 51775 techOrder: ['hls', 'html5', 'flash'], 51776 html5: { 51777 nativeVideoTracks : false, 51778 nativeAudioTracks : false, 51779 hls: { 51780 overrideNative: true, 51781 } 51782 }, 51783 }); 51784 51785 51786 51787 if (opts && opts.showPosterOnVideoEnnd) { 51788 vid.on('ended', function() { 51789 51790 51791 try { vid.cancelFullScreen() } catch (e) {} 51792 51793 51794 vid.pause(); 51795 if (typeof vid.currentTime === 'function') vid.currentTime(0); 51796 51797 51798 vid.posterImage.show(); 51799 51800 51801 $(vid.el()).addClass('gsc-ended'); 51802 }); 51803 } 51804 51805 51806 vid.on('play', function (e) { 51807 el.siblings('.vjs-loading-spinner').hide(); 51808 }) 51809 } else { 51810 vid = videojs(el.get(0)); 51811 } 51812 51813 return vid; 51814 } 51815 51816 function playVideoJS(el) { 51817 51818 51819 if (window.globalGetParams && globalGetParams.headless) return; 51820 51821 var vid = initVideoJS(el); 51822 setTimeout(function () { 51823 51824 if (vid.muted()) { 51825 vid.muted(false); 51826 vid.controls(true); 51827 vid.pause(); 51828 $('.video-slide-muted').removeClass('video-slide-muted'); 51829 vid.currentTime(0); 51830 } 51831 51832 vid.play(); 51833 $('body').addClass('slideshow-vid-playing'); 51834 51835 vid.on('ended', function() { 51836 $('body').removeClass('slideshow-vid-playing'); 51837 }); 51838 51839 vid.on('timeupdate', function() { 51840 if (vid.paused()) { 51841 $('body').removeClass('slideshow-vid-playing'); 51842 } else { 51843 $('body').addClass('slideshow-vid-playing'); 51844 } 51845 }); 51846 51847 }, 1); 51848 } 51849 51850 51851 GscResponsiveSlider.initVideoJS = initVideoJS; 51852 GscResponsiveSlider.playVideoJS = playVideoJS; 51853 51854 return me; 51855 51856})(); 51857 51858var globalHas3dResult; 51859 51860 51861var globalHas3d = (function(){ 51862 if(globalHas3dResult !== undefined ) { 51863 return globalHas3dResult; 51864 } 51865 var el = document.createElement('p'), 51866 globalHas3d, 51867 transforms = { 51868 'webkitTransform':'-webkit-transform', 51869 'OTransform':'-o-transform', 51870 'msTransform':'-ms-transform', 51871 'MozTransform':'-moz-transform', 51872 'transform':'transform' 51873 }; 51874 51875 51876 if(!document.body) { 51877 return true; 51878 } 51879 51880 51881 document.body.insertBefore(el, null); 51882 51883 var style = window.getComputedStyle(el); 51884 51885 for(var t in transforms){ 51886 if (transforms.hasOwnProperty(t)) { 51887 if( el.style[t] !== undefined ){ 51888 el.style[t] = 'translate3d(1px,1px,1px)'; 51889 globalHas3d = style.getPropertyValue(transforms[t]); 51890 } 51891 } 51892 } 51893 51894 document.body.removeChild(el); 51895 51896 globalHas3dResult = (globalHas3d !== undefined && globalHas3d.length > 0 && globalHas3d !== "none") 51897 51898 return globalHas3dResult; 51899}()); 51900 51901function determineDownloadSpeed(callback) { 51902 51903 var oProgress = document.getElementById("progress"); 51904 var startTime, endTime; 51905 var download = new Image(); 51906 download.onload = function () { 51907 endTime = (new Date()).getTime(); 51908 callback && callback(); 51909 } 51910 51911 download.onerror = function (err, msg) { 51912 oProgress.innerHTML = "Invalid image, or error downloading"; 51913 } 51914 51915 startTime = (new Date()).getTime(); 51916 var cacheBuster = "?nnn=" + startTime;
51917 download.src = imageAddr + cacheBuster; 51918 51919 51920 51921 51922} 51923 51924//</script> 51925 51926/** 51927 51928@file 51929Autoplayer/controller for a slideshow. 51930@documentation http://wiki.goldeneraproductions.org/wiki/Gsc_headers 51931 51932*/ 51933 51934var GscSliderAutoplay = function(slideshowWrapperDom, slideshowRef, opts) { 51935 // mix in the opts 51936 this.slideshowWrapperDom = slideshowWrapperDom; 51937 this.slideshowRef = slideshowRef; 51938 if (opts) for (var key in opts) this[key] = opts[key]; 51939} 51940 51941GscSliderAutoplay.prototype = (function() { 51942 51943 var me = {}; 51944 51945 // defaults 51946 me.autosliding = false; // need explicit start() to start sliding 51947 me.interval = 9000; 51948 51949 // initializes (hookups, create DOM elements etm.), does not start autoplay yet 51950 me.init = function() { 51951 51952 var self = this; 51953 51954 // don't to it for IE 51955 if (globalIsIE) return this; 51956 51957 // sanity checks 51958 if (!this.slideshowWrapperDom) { 51959 window.console && console.error('No slideshowWrapperDom supplied - cannot initialize autoslider!'); 51960 return this; 51961 } 51962 51963 if (!this.slideshowRef) { 51964 window.console && console.error('No slideshowRef supplied - cannot initialize autoslider!'); 51965 return this; 51966 } 51967 51968 if (!(this.slideshowWrapperDom instanceof jQuery)) this.slideshowWrapperDom = $(this.slideshowWrapperDom); 51969 51970 // create the DOM elements 51971 this.slideshowWrapperDom.append('<div class="slide-gutter"><div class="slider-artifact">» Menu</div><div class="slider-play-button paused"></div></div>'); 51972 this.slideshowWrapperDom.find('.slide-wrapper').append('<div class="slideshow-autoslide-strip-holder"><div class="slideshow-autoslide-strip"></div></div>'); 51973 this.stripDom = this.slideshowWrapperDom.find('.slideshow-autoslide-strip'); 51974 this.playbuttonDom = this.slideshowWrapperDom.find('.slider-play-button'); 51975 51976 this.playbuttonDom.on('click', function() { 51977 if (!self.autosliding) { 51978 self.start(); 51979 } else { 51980 self.stop(); 51981 } 51982 }); 51983 51984 // hook up with the slideshow 51985 this.slideshowWrapperDom.bind('userInteraction', function() { 51986 self.stop(); 51987 }); 51988 this.slideshowWrapperDom.bind('videoAutoplay', function() { 51989 // console.log('videoAutoplay event received'); 51990 _.defer(function() { 51991 self.stop(); 51992 }); 51993 setTimeout(function() { 51994 self.stop(); 51995 }, 500); 51996 }); 51997 51998 // console.log('autoplayer is listening for videoAutoplay'); 51999 52000 setTimeout(function(){ 52001 $(window).resize(); 52002 }, 2000); 52003 52004 return this; 52005 52006 } 52007 52008 // animate the strip 52009 me.animateToNext = function() { 52010 52011 var self = this; 52012 52013 this.stripDom.width(0); 52014 52015 // the end, my friend, the end ... 52016 if (!this.slideshowRef.isCircularClone() && this.slideshowRef.currentSlideIdx() == this.slideshowRef.totalSlides() - 1) { 52017 this.stop(); 52018 _.defer(function() { 52019 self.stop(); 52020 }); 52021 return; 52022 } 52023 52024 globalAnimationController.cancelAnimation(this.stripDom, true); 52025 52026 globalAnimationController.animate(this.stripDom, { width: this.stripDom.parent().outerWidth(true) }, 52027 this.interval + 'ms linear', 52028 function() { 52029 self.slideshowRef.sliderMoveNext(); 52030 _.defer(function() { 52031 self.animateToNext(); 52032 }); 52033 } 52034 ); 52035 52036 } 52037 52038 52039 // start autoplaying 52040 me.start = function() { 52041 52042 // console.log('autosliding started'); 52043 52044 // don't to it for IE 52045 if (globalIsIE) return this; 52046 52047 this.animateToNext(); 52048 this.playbuttonDom.removeClass('paused'); 52049 this.autosliding = true; 52050 return this; 52051 } 52052 52053 // stop autoplaying 52054 me.stop = function() { 52055 globalAnimationController.cancelAnimation(this.stripDom, true); 52056 this.stripDom.width(0); 52057 this.playbuttonDom.addClass('paused'); 52058 this.autosliding = false;
52059 return this; 52060 } 52061 52062 return me; 52063 52064})(); 52065 52066 52067/** 52068 52069@file 52070Simple animation controller. Had to make a JS animation controller, partially because CSS transitions don't always fire the transitionEnd events (see spec http://dev.w3.org/csswg/css-transitions/#transition-events), which is required for precise control of animation (i.e. in some cases, we need to ensure 52071that the transition completion callback is called and executed before the next transition 52072begins). 52073 52074FIXME: Falls back to jQuery.animate() if no CSS transition capability (uses modernizr for detection). 52075 52076Requires: 52077--------- 52078 52079jQuery, underscore.js, modernizr.js 52080 52081Usage: 52082------ 52083 52084```javascript 52085globalAnimationController.animate2d( 52086 aDomElem, 52087 {x: -100, y:0}, 52088 '.8s cubic-bezier(0.165, 0.840, 0.440, 1.000)', 52089 function() { ... } 52090); 52091``` 52092 52093*/ 52094 52095var AnimationController = function(opts) { 52096 // mix in the opts 52097 if (opts) for (var key in opts) this[key] = opts[key]; 52098} 52099 52100AnimationController.prototype = (function() { 52101 52102 var me = {}; 52103 52104 var transitionTransformProps = { 52105 'WebkitTransform': '-webkit-transform', 52106 'MozTransform': '-moz-transform', 52107 'msTransform': '-ms-transform', 52108 'OTransform': '-o-transform', 52109 'transform': 'transform', 52110 }; 52111 52112 me.callbacks = {}; 52113 me.maxAnimationId = 0; 52114 52115 // for 2D transforms (implemented using translate3d for performance) 52116 me.animate2d = function(aDomElem, aTransform2d, aTransition, aCallbackFunc) { 52117 52118 logger.debug('animate2d called for: ' + aTransform2d.x + ', ' + aTransition); 52119 52120 var myController = this; 52121 if (!(aDomElem instanceof jQuery))Â aDomElem = $(aDomElem); 52122 52123 var myAnimationId = aDomElem.attr('data-animation-id'); 52124 if (!myAnimationId) { 52125 myAnimationId = myController.maxAnimationId++; 52126 aDomElem.attr('data-animation-id', myAnimationId); 52127 } 52128 52129 myController.cancelAnimation(aDomElem); 52130 52131 var myCaller = arguments.callee.caller.parent; 52132 _.defer(function() { 52133 52134 if (Modernizr.csstransitions && Modernizr.csstransforms3d) { 52135 52136 // FIXME add to the transition property, don't override (otherwise will cobber other) 52137 if (aTransition != 'none') { 52138 aDomElem.css( 52139 Modernizr.prefixed('transition'), 52140 transitionTransformProps[Modernizr.prefixed('transform')] + ' ' + aTransition 52141 ); 52142 52143 /* 52144 // add some extra CSS that might speed things up 52145 .css('-webkit-backface-visibility', 'hidden') // @info http://css-tricks.com/almanac/properties/b/backface-visibility/ 52146 .css('-webkit-transform-style', 'preserve-3d') // @info http://css-tricks.com/almanac/properties/t/transform-style/ 52147 .css('transform-style', 'preserve-3d'); 52148 */ 52149 52150 } else { 52151 aDomElem.css(Modernizr.prefixed('transition'), ''); 52152 } 52153 52154 aDomElem.css(Modernizr.prefixed('transform'), 'translate3d(' + aTransform2d.x + 'px' + ',' + aTransform2d.y + 'px,0)'); 52155 52156 } else { 52157 52158 // FIXME animation fallback !!! 52159 52160 /* 52161 aDomElem.animate({ 52162 left: aTransform2d.x + 'px' 52163 }); 52164 */ 52165 52166 aDomElem.css('left', aTransform2d.x + 'px'); 52167 52168 if (aTransform2d.y != 0) aDomElem.animate({ 52169 top: aTransform2d.y + 'px' 52170 }); 52171 52172 } 52173 52174 myController.callbacks[myAnimationId] = [aCallbackFunc]; 52175 52176 logger.debug('animation started to: ' + aTransform2d.x); 52177 52178 if (aTransition != 'none' && Modernizr.csstransitions && Modernizr.csstransforms3d) { 52179 aDomElem.on('webkitTransitionEnd oTransitionEnd otransitionend transitionend msTransitionEnd MSTransitionEnd gcuiTransitionCancelled', function(e) { 52180 if (e.target != aDomElem[0]) return true; // it's not us 52181 aDomElem.unbind('webkitTransitionEnd oTransitionEnd otransitionend transitionend msTransitionEnd MSTransitionEnd gcuiTransitionCancelled'); 52182 logger.debug('transitionEnd called on: ' + aTransform2d.x); 52183 _cancelAnimation(aDomElem, myCaller); 52184 }); 52185 } else { 52186 52187 logger.debug('wrapping up no-transition on: ' + aTransform2d.x); 52188 52189 // if we are not animating at all, just call the animation compeltiong calls right now 52190 if (aTransition == 'none') { 52191 _cancelAnimation(aDomElem, myCaller); 52192 } else { 52193 52194 if (aTransition.match(/ms/)) { 52195 var myTime = parseFloat(aTransition.replace(/ms .*$/, '')); 52196 } else { 52197 var myTime = parseFloat(aTransition.replace(/s .*$/, '')) * 1000; 52198 } 52199 52200 setTimeout(function() { 52201 _cancelAnimation(aDomElem, myCaller); 52202 }, myTime); 52203 52204 } 52205 } 52206 52207 }); 52208 52209 }; 52210 52211 // for generic animation 52212 me.animate = function(aDomElem, aProps, aTransition, aCallbackFunc) { 52213 52214 var myController = this; 52215 if (!(aDomElem instanceof jQuery))Â aDomElem = $(aDomElem); 52216 52217 var myAnimationId = aDomElem.attr('data-animation-id'); 52218 if (!myAnimationId) { 52219 myAnimationId = myController.maxAnimationId++; 52220 aDomElem.attr('data-animation-id', myAnimationId); 52221 } 52222 52223 myController.cancelAnimation(aDomElem); 52224 52225 var myCaller = arguments.callee.caller.parent; 52226 _.defer(function() { 52227 52228 if (Modernizr.csstransitions) { 52229 52230 // FIXME add to the transition property, don't override (otherwise will c
52230obber other) 52231 if (aTransition != 'none') { 52232 aDomElem.css(Modernizr.prefixed('transition'), 52233 /* 52234 _.reduce(aProps, function(m, v, k) { 52235 return m.concat([k + ' ' + aTransition]); 52236 }, []).join(', ') 52237 */ 52238 'all ' + aTransition 52239 ); 52240 52241 /* 52242 // add some extra CSS that might speed things up 52243 .css('-webkit-backface-visibility', 'hidden') // @info http://css-tricks.com/almanac/properties/b/backface-visibility/ 52244 .css('-webkit-transform-style', 'preserve-3d') // @info http://css-tricks.com/almanac/properties/t/transform-style/ 52245 .css('transform-style', 'preserve-3d'); 52246 */ 52247 52248 } else { 52249 aDomElem.css(Modernizr.prefixed('transition'), ''); 52250 } 52251 52252 } 52253 52254 _.each(aProps, function(v, k) { 52255 aDomElem.css(k, v); 52256 }); 52257 52258 myController.callbacks[myAnimationId] = [aCallbackFunc]; 52259 52260 if (aTransition != 'none' && Modernizr.csstransitions) { 52261 52262 aDomElem.on('webkitTransitionEnd oTransitionEnd otransitionend transitionend msTransitionEnd MSTransitionEnd gcuiTransitionCancelled', function(e) { 52263 if (e.target != aDomElem[0]) return true; // it's not us 52264 aDomElem.unbind('webkitTransitionEnd oTransitionEnd otransitionend transitionend msTransitionEnd MSTransitionEnd gcuiTransitionCancelled'); 52265 _cancelAnimation(aDomElem, myCaller); 52266 }); 52267 } else { 52268 // if we are not animating at all, just call the animation compeltiong calls right now 52269 if (aTransition == 'none') { 52270 _cancelAnimation(aDomElem, myCaller); 52271 } else { 52272 52273 if (aTransition.match(/ms/)) { 52274 var myTime = parseFloat(aTransition.replace(/ms .*$/, '')); 52275 } else { 52276 var myTime = parseFloat(aTransition.replace(/s .*$/, '')) * 1000; 52277 } 52278 52279 setTimeout(function() { 52280 _cancelAnimation(aDomElem, myCaller); 52281 }, myTime); 52282 52283 } 52284 } 52285 52286 }); 52287 52288 }; 52289 52290 // this is an internal one 52291 var _cancelAnimation = function(aDomElem, aCaller) { 52292 52293 var myAnimationId = aDomElem.attr('data-animation-id'); 52294 52295 // FIXME only remove own transitions, don't clobber all 52296 Modernizr.prefixed('transition') && aDomElem.css(Modernizr.prefixed('transition'), ''); 52297 52298 // call all callbacks for the element first 52299 for (var a in me.callbacks[myAnimationId]) { 52300 me.callbacks[myAnimationId][a] && me.callbacks[myAnimationId][a].apply(aCaller); 52301 } 52302 me.callbacks[myAnimationId] = []; 52303 52304 } 52305 52306 // call this to cancel any ongoing transitions on the aDomElem 52307 me.cancelAnimation = function(aDomElem, aSkipCallbacks) { 52308 if (!(aDomElem instanceof jQuery))Â aDomElem = $(aDomElem); 52309 if (aSkipCallbacks) this.callbacks[aDomElem.attr('data-animation-id')] = []; 52310 aDomElem.trigger('gcuiTransitionCancelled'); 52311 } 52312 52313 return me; 52314 52315})(); 52316 52317globalAnimationController = new AnimationController(); 52318 52319 52320//<script> 52321 52322 52323 52324 var GscResponsiveFlyouts = function(opts) { 52325 52326 for (var key in opts) this[key] = opts[key]; 52327} 52328 52329GscResponsiveFlyouts.prototype = (function() { 52330 52331 var me = {}; 52332 52333 me.flyoutDomParent = $('.slideshow-wrapper'); 52334 me.slideshowHolderDom = $('.slideshow-holder'); 52335 52336 52337 me.init = function() { 52338 52339 var that = this; 52340 52341 if (!(this.flyoutDomParent instanceof jQuery)) this.flyoutDomParent = $(this.flyoutDomParent); 52342 if (!(this.slideshowHolderDom instanceof jQuery)) this.slideshowHolderDom = $(this.slideshowHolderDom); 52343 52344 this.flyoutDomParent.find('.flyout > .text-flyout .text-flyout.submenu').each(function() { 52345 $(this).css('margin-top', '-' + Math.floor($(this).height() / 2) + 'px'); 52346 }); 52347 52348 me.resize(); 52349 $(window).on('resize', function() { that.resize(); }); 52350 52351 if(cookieMonster.get("mobile-menu") != null && cookieMonster.get("mobile-menu") == "closed") { 52352 $('.slideshow-left-nav-mobile').removeClass('open'); 52353 } 52354 52355 wireupTotalTouchControl('js-mobile-flyout-open', function(e) { 52356 52357 $('.slideshow-left-nav-mobile').toggleClass('open'); 52358 if($('.slideshow-left-nav-mobile').hasClass('open')) { 52359 cookieMonster.set("mobile-menu", "opened"); 52360 } else { 52361 cookieMonster.set("mobile-menu", "closed"); 52362 } 52363 }); 52364 52365 }; 52366 52367 me.resize = function() { 52368 52369 var slideshowWidth = this.slideshowHolderDom.width(); 52370 var slideshowLeft = this.slideshowHolderDom.offset().left; 52371 var slideshowTop = this.slideshowHolderDom.offset().top; 52372 var slideshowBottom = slideshowTop + this.slideshowHolderDom.height(); 52373 52374 if ($(window).width() < 860) { 52375 52376 return; 52377 52378 52379 52380 this.slideshowHolderDom.find('.flyout > .text-flyout > li.parent').each(function() { 52381 var mySub = $(this).find('ul.text-flyout.submenu'); 52382 if (!mySub[0]) return; 52383 52384 mySub.addClass('force-opened'); 52385 mySub[0].offsetHeight; 52386 52387 var myNewHeight = -(Math.floor(mySub.outerHeight(true) / 2) - Math.floor($(this).outerHeight(true) / 2) - 10); 52388 mySub.css('top', myNewHeight + 'px'); 52389 52390 52391 52392 52393 mySub.removeClass('force-opened'); 52394 52395 }); 52396 52397 52398 52399 } else { 52400 52401 52402 52403 52404 this.slideshowHolderDom.find('.flyout li.parent ul.thumbs-flyout').each(function() { 52405 52406 $(this).css('height', 'auto'); 52407 52408 var myLeft = $(this).offset().left - slideshowLeft; 52409 if (isRTL) myLeft = (slideshowLeft + slideshowWidth) - (myLeft + $(this).width()); 52410 52411 $(this).css('width', (slideshowWidth - myLeft) + 'px').css('top', '0px'); 52412 52413 var myElem = $(this); 52414 52415 $(this).find('li.hidden').removeClass('hidden'); 52416 52417 52418 52419 52420 52421 var myBottom = $(this).offset().top + $(this).height(); 52422 if (myBottom > slideshowBottom) { 52423 52424 52425 var myMenuitemBottom = $(this).parents('li.parent').offset().top + $(this).parents('li.parent').height(); 52426 if (myMenuitemBottom < slideshowBottom) { 52427 $(this).css('top', (slideshowBottom - myBottom) + 'px'); 52428 } else { 52429 $(this).css('top', (myMenuitemBottom - myBottom) + 'px'); 52430 } 52431 52432 52433 52434 52435 52436 52437 52438 var infiniteGuard = 5000; 52439 52440 var myTop = $(this).offset().top; 52441 52442 while (myTop < slideshowTop) { 52443 52444 var myElems = $(this).find('li').not('.hidden'); 52445 myElems.eq(myElems.length-1).addClass('hidden'); 52446 52447 52448 52449 52450 52451 52452 myBottom = $(this).offset().top + $(this).height(); 52453 var myCurrentCSSTop = parseInt($(this).css('top').replace(/px/, '')); 52454 52455 if (myMenuitemBottom < slideshowBottom) { 52456 $(this).css('top', myCurrentCSSTop + (slideshowBottom - myBottom) + 'px'); 52457 } else { 52458 $(this).css('top', myCurrentCSSTop + (myMenuitemBottom - myBottom) + 'px'); 52459 } 52460 52461 52462 52463 myBottom = $(this).offset().top + $(this).height(); 52464 myTop = $(this).offset().top; 52465 52466 if (infiniteGuard-- < 0) { 52467 window.console && console.error && console.error('ERROR: infinite loop detected'); 52468 break; 52469 } 52470 52471 if (Math.floor(myBottom) > slideshowBottom) { 52472 $(this).css('outline', '2px solid red'); 52473 window.console && console.error && console.error('ERROR: resizing error'); 52474 } 52475 52476 } 52477 52478 } 52479 52480 }); 52481 52482 52483 52484 } 52485 52486 }; 52487 52488 return me; 52489 52490})(); 52491 52492 52493//</script> 52494 52495/*! 52496 * VERSION: 1.11.2 52497 * DATE: 2013-11-20 52498 * UPDATES AND DOCS AT: http://www.greensock.com 52499 * 52500 * Includes all of the following: TweenLite, TweenMax, TimelineLite, TimelineMax, EasePack, CSSPlugin, RoundPropsPlugin, BezierPlugin, AttrPlugin, DirectionalRotationPlugin 52501 * 52502 * @license Copyright (c) 2008-2013, GreenSock. All rights reserved. 52503 * This work is subject to the terms at http://www.greensock.com/terms_of_use.html or for
52504 * Club GreenSock members, the software agreement that was issued with your membership. 52505 * 52506 * @author: Jack Doyle, [email protected] 52507 **/ 52508(window._gsQueue||(window._gsQueue=[])).push(function(){"use strict";window._gsDefine("TweenMax",["core.Animation","core.SimpleTimeline","TweenLite"],function(t,e,i){var s=[].slice,r=function(t,e,s){i.call(this,t,e,s),this._cycle=0,this._yoyo=this.vars.yoyo===!0,this._repeat=this.vars.repeat||0,this._repeatDelay=this.vars.repeatDelay||0,this._dirty=!0,this.render=r.prototype.render},n=1e-10,a=i._internals.isSelector,o=i._internals.isArray,h=r.prototype=i.to({},.1,{}),l=[];r.version="1.11.2",h.constructor=r,h.kill()._gc=!1,r.killTweensOf=r.killDelayedCallsTo=i.killTweensOf,r.getTweensOf=i.getTweensOf,r.ticker=i.ticker,h.invalidate=function(){return this._yoyo=this.vars.yoyo===!0,this._repeat=this.vars.repeat||0,this._repeatDelay=this.vars.repeatDelay||0,this._uncache(!0),i.prototype.invalidate.call(this)},h.updateTo=function(t,e){var s,r=this.ratio;e&&this.timeline&&this._startTime<this._timeline._time&&(this._startTime=this._timeline._time,this._uncache(!1),this._gc?this._enabled(!0,!1):this._timeline.insert(this,this._startTime-this._delay));for(s in t)this.vars[s]=t[s];if(this._initted)if(e)this._initted=!1;else if(this._notifyPluginsOfEnabled&&this._firstPT&&i._onPluginEvent("_onDisable",this),this._time/this._duration>.998){var n=this._time;this.render(0,!0,!1),this._initted=!1,this.render(n,!0,!1)}else if(this._time>0){this._initted=!1,this._init();for(var a,o=1/(1-r),h=this._firstPT;h;)a=h.s+h.c,h.c*=o,h.s=a-h.c,h=h._next}return this},h.render=function(t,e,i){this._initted||0===this._duration&&this.vars.repeat&&this.invalidate();var s,r,a,o,h,_,u,p,f=this._dirty?this.totalDuration():this._totalDuration,c=this._time,m=this._totalTime,d=this._cycle,g=this._duration;if(t>=f?(this._totalTime=f,this._cycle=this._repeat,this._yoyo&&0!==(1&this._cycle)?(this._time=0,this.ratio=this._ease._calcEnd?this._ease.getRatio(0):0):(this._time=g,this.ratio=this._ease._calcEnd?this._ease.getRatio(1):1),this._reversed||(s=!0,r="onComplete"),0===g&&(p=this._rawPrevTime,(0===t||0>p||p===n)&&p!==t&&(i=!0,p>n&&(r="onReverseComplete")),this._rawPrevTime=p=!e||t?t:n)):1e-7>t?(this._totalTime=this._time=this._cycle=0,this.ratio=this._ease._calcEnd?this._ease.getRatio(0):0,(0!==m||0===g&&this._rawPrevTime>n)&&(r="onReverseComplete",s=this._reversed),0>t?(this._active=!1,0===g&&(this._rawPrevTime>=0&&(i=!0),this._rawPrevTime=p=!e||t?t:n)):this._initted||(i=!0)):(this._totalTime=this._time=t,0!==this._repeat&&(o=g+this._repeatDelay,this._cycle=this._totalTime/o>>0,0!==this._cycle&&this._cycle===this._totalTime/o&&this._cycle--,this._time=this._totalTime-this._cycle*o,this._yoyo&&0!==(1&this._cycle)&&(this._time=g-this._time),this._time>g?this._time=g:0>this._time&&(this._time=0)),this._easeType?(h=this._time/g,_=this._easeType,u=this._easePower,(1===_||3===_&&h>=.5)&&(h=1-h),3===_&&(h*=2),1===u?h*=h:2===u?h*=h*h:3===u?h*=h*h*h:4===u&&(h*=h*h*h*h),this.ratio=1===_?1-h:2===_?h:.5>this._time/g?h/2:1-h/2):this.ratio=this._ease.getRatio(this._time/g)),c===this._time&&!i&&d===this._cycle)return m!==this._totalTime&&this._onUpdate&&(e||this._onUpdate.apply(this.vars.onUpdateScope||this,this.vars.onUpdateParams||l)),void 0;if(!this._initted){if(this._init(),!this._initted||this._gc)return;this._time&&!s?this.ratio=this._ease.getRatio(this._time/g):s&&this._ease._calcEnd&&(this.ratio=this._ease.getRatio(0===this._time?0:1))}for(this._active||!this._paused&&this._time!==c&&t>=0&&(this._active=!0),0===m&&(this._startAt&&(t>=0?this._startAt.render(t,e,i):r||(r="_dummyGS")),this.vars.onStart&&(0!==this._totalTime||0===g)&&(e||this.vars.onStart.apply(this.vars.onStartScope||this,this.vars.onStartParams||l))),a=this._firstPT;a;)a.f?a.t[a.p](a.c*this.ratio+a.s):a.t[a.p]=a.c*this.ratio+a.s,a=a._next;this._onUpdate&&(0>t&&this._startAt&&this._startTime&&this._startAt.render(t,e,i),e||this._onUpdate.apply(this.vars.onUpdateScope||this,this.vars.onUpdateParams||l)),this._cycle!==d&&(e||this._gc||this.vars.onRepeat&&this.vars.onRepeat.apply(this.vars.onRepeatScope||this,this.vars.onRepeatParams||l)),r&&(this._gc||(0>t&&this._startAt&&!this._onUpdate&&this._startTime&&this._startAt.render(t,e,i),s&&(this._timeline.autoRemoveChildren&&this._enabled(!1,!1),this._active=!1),!e&&this.vars[r]&&this.vars[r].apply(this.vars[r+"Scope"]||this,this.vars[r+"Params"]||l),0===g&&this._rawPrevTime===n&&p!==n&&(this._rawPrevTime=0)))}
vendor: 4,147 bytes, line 52508
52508,r.to=function(t,e,i){return new r(t,e,i)},r.from=function(t,e,i){return i.runBackwards=!0,i.immediateRender=0!=i.immediateRender,new r(t,e,i)},r.fromTo=function(t,e,i,s){return s.startAt=i,s.immediateRender=0!=s.immediateRender&&0!=i.immediateRender,new r(t,e,s)},r.staggerTo=r.allTo=function(t,e,n,h,_,u,p){h=h||0;var f,c,m,d,g=n.delay||0,v=[],y=function(){n.onComplete&&n.onComplete.apply(n.onCompleteScope||this,arguments),_.apply(p||this,u||l)};for(o(t)||("string"==typeof t&&(t=i.selector(t)||t),a(t)&&(t=s.call(t,0))),f=t.length,m=0;f>m;m++){c={};for(d in n)c[d]=n[d];c.delay=g,m===f-1&&_&&(c.onComplete=y),v[m]=new r(t[m],e,c),g+=h}return v},r.staggerFrom=r.allFrom=function(t,e,i,s,n,a,o){return i.runBackwards=!0,i.immediateRender=0!=i.immediateRender,r.staggerTo(t,e,i,s,n,a,o)},r.staggerFromTo=r.allFromTo=function(t,e,i,s,n,a,o,h){return s.startAt=i,s.immediateRender=0!=s.immediateRender&&0!=i.immediateRender,r.staggerTo(t,e,s,n,a,o,h)},r.delayedCall=function(t,e,i,s,n){return new r(e,0,{delay:t,onComplete:e,onCompleteParams:i,onCompleteScope:s,onReverseComplete:e,onReverseCompleteParams:i,onReverseCompleteScope:s,immediateRender:!1,useFrames:n,overwrite:0})},r.set=function(t,e){return new r(t,0,e)},r.isTweening=function(t){return i.getTweensOf(t,!0).length>0};var _=function(t,e){for(var s=[],r=0,n=t._first;n;)n instanceof i?s[r++]=n:(e&&(s[r++]=n),s=s.concat(_(n,e)),r=s.length),n=n._next;return s},u=r.getAllTweens=function(e){return _(t._rootTimeline,e).concat(_(t._rootFramesTimeline,e))};r.killAll=function(t,i,s,r){null==i&&(i=!0),null==s&&(s=!0);var n,a,o,h=u(0!=r),l=h.length,_=i&&s&&r;for(o=0;l>o;o++)a=h[o],(_||a instanceof e||(n=a.target===a.vars.onComplete)&&s||i&&!n)&&(t?a.totalTime(a.totalDuration()):a._enabled(!1,!1))},r.killChildTweensOf=function(t,e){if(null!=t){var n,h,l,_,u,p=i._tweenLookup;if("string"==typeof t&&(t=i.selector(t)||t),a(t)&&(t=s(t,0)),o(t))for(_=t.length;--_>-1;)r.killChildTweensOf(t[_],e);else{n=[];for(l in p)for(h=p[l].target.parentNode;h;)h===t&&(n=n.concat(p[l].tweens)),h=h.parentNode;for(u=n.length,_=0;u>_;_++)e&&n[_].totalTime(n[_].totalDuration()),n[_]._enabled(!1,!1)}}};var p=function(t,i,s,r){i=i!==!1,s=s!==!1,r=r!==!1;for(var n,a,o=u(r),h=i&&s&&r,l=o.length;--l>-1;)a=o[l],(h||a instanceof e||(n=a.target===a.vars.onComplete)&&s||i&&!n)&&a.paused(t)};return r.pauseAll=function(t,e,i){p(!0,t,e,i)},r.resumeAll=function(t,e,i){p(!1,t,e,i)},r.globalTimeScale=function(e){var s=t._rootTimeline,r=i.ticker.time;return arguments.length?(e=e||n,s._startTime=r-(r-s._startTime)*s._timeScale/e,s=t._rootFramesTimeline,r=i.ticker.frame,s._startTime=r-(r-s._startTime)*s._timeScale/e,s._timeScale=t._rootTimeline._timeScale=e,e):s._timeScale},h.progress=function(t){return arguments.length?this.totalTime(this.duration()*(this._yoyo&&0!==(1&this._cycle)?1-t:t)+this._cycle*(this._duration+this._repeatDelay),!1):this._time/this.duration()},h.totalProgress=function(t){return arguments.length?this.totalTime(this.totalDuration()*t,!1):this._totalTime/this.totalDuration()},h.time=function(t,e){return arguments.length?(this._dirty&&this.totalDuration(),t>this._duration&&(t=this._duration),this._yoyo&&0!==(1&this._cycle)?t=this._duration-t+this._cycle*(this._duration+this._repeatDelay):0!==this._repeat&&(t+=this._cycle*(this._duration+this._repeatDelay)),this.totalTime(t,e)):this._time},h.duration=function(e){return arguments.length?t.prototype.duration.call(this,e):this._duration},h.totalDuration=function(t){return arguments.length?-1===this._repeat?this:this.duration((t-this._repeat*this._repeatDelay)/(this._repeat+1)):(this._dirty&&(this._totalDuration=-1===this._repeat?999999999999:this._duration*(this._repeat+1)+this._repeatDelay*this._repeat,this._dirty=!1),this._totalDuration)},h.repeat=function(t){return arguments.length?(this._repeat=t,this._uncache(!0)):this._repeat},h.repeatDelay=function(t){return arguments.length?(this._repeatDelay=t,this._uncache(!0)):this._repeatDelay},h.yoyo=function(t){return arguments.length?(this._yoyo=t,this):this._yoyo},r},!0),window._gsDefine("TimelineLite",["core.Animation","core.SimpleTimeline","TweenLite"],function(t,e,
52508i){var s=function(t){e.call(this,t),this._labels={},this.autoRemoveChildren=this.vars.autoRemoveChildren===!0,this.smoothChildTiming=this.vars.smoothChildTiming===!0,this._sortChildren=!0,this._onUpdate=this.vars.onUpdate;var i,s,r=this.vars;for(s in r)i=r[s],a(i)&&-1!==i.join("").indexOf("{self}")&&(r[s]=this._swapSelfInParams(i));a(r.tweens)&&this.add(r.tweens,0,r.align,r.stagger)},r=1e-10,n=i._internals.isSelector,a=i._internals.isArray,o=[],h=function(t){var e,i={};for(e in t)i[e]=t[e];return i},l=function(t,e,i,s){t._timeline.pause(t._startTime),e&&e.apply(s||t._timeline,i||o)},_=o.slice,u=s.prototype=new e;return s.version="1.11.0",u.constructor=s,u.kill()._gc=!1,u.to=function(t,e,s,r){return e?this.add(new i(t,e,s),r):this.set(t,s,r)},u.from=function(t,e,s,r){return this.add(i.from(t,e,s),r)},u.fromTo=function(t,e,s,r,n){return e?this.add(i.fromTo(t,e,s,r),n):this.set(t,r,n)},u.staggerTo=function(t,e,r,a,o,l,u,p){var f,c=new s({onComplete:l,onCompleteParams:u,onCompleteScope:p});for("string"==typeof t&&(t=i.selector(t)||t),n(t)&&(t=_.call(t,0)),a=a||0,f=0;t.length>f;f++)r.startAt&&(r.startAt=h(r.startAt)),c.to(t[f],e,h(r),f*a);return this.add(c,o)},u.staggerFrom=function(t,e,i,s,r,n,a,o){return i.immediateRender=0!=i.immediateRender,i.runBackwards=!0,this.staggerTo(t,e,i,s,r,n,a,o)},u.staggerFromTo=function(t,e,i,s,r,n,a,o,h){return s.startAt=i,s.immediateRender=0!=s.immediateRender&&0!=i.immediateRender,this.staggerTo(t,e,s,r,n,a,o,h)},u.call=function(t,e,s,r){return this.add(i.delayedCall(0,t,e,s),r)},u.set=function(t,e,s){return s=this._parseTimeOrLabel(s,0,!0),null==e.immediateRender&&(e.immediateRender=s===this._time&&!this._paused),this.add(new i(t,0,e),s)},s.exportRoot=function(t,e){t=t||{},null==t.smoothChildTiming&&(t.smoothChildTiming=!0);var r,n,a=new s(t),o=a._timeline;for(null==e&&(e=!0),o._remove(a,!0),a._startTime=0,a._rawPrevTime=a._time=a._totalTime=o._time,r=o._first;r;)n=r._next,e&&r instanceof i&&r.target===r.vars.onComplete||a.add(r,r._startTime-r._delay),r=n;return o.add(a,0),a},u.add=function(r,n,o,h){var l,_,u,p,f,c;if("number"!=typeof n&&(n=this._parseTimeOrLabel(n,0,!0,r)),!(r instanceof t)){if(r instanceof Array||r&&r.push&&a(r)){for(o=o||"normal",h=h||0,l=n,_=r.length,u=0;_>u;u++)a(p=r[u])&&(p=new s({tweens:p})),this.add(p,l),"string"!=typeof p&&"function"!=typeof p&&("sequence"===o?l=p._startTime+p.totalDuration()/p._timeScale:"start"===o&&(p._startTime-=p.delay())),l+=h;return this._uncache(!0)}if("string"==typeof r)return this.addLabel(r,n);if("function"!=typeof r)throw"Cannot add "+r+" into the timeline; it is not a tween, timeline, function, or string.";r=i.delayedCall(0,r)}if(e.prototype.add.call(this,r,n),this._gc&&!this._paused&&this._duration<this.duration())for(f=this,c=f.rawTime()>r._startTime;f._gc&&f._timeline;)f._timeline.smoothChildTiming&&c?f.totalTime(f._totalTime,!0):f._enabled(!0,!1),f=f._timeline;return this},u.remove=function(e){if(e instanceof t)return this._remove(e,!1);if(e instanceof Array||e&&e.push&&a(e)){for(var i=e.length;--i>-1;)this.remove(e[i]);return this}return"string"==typeof e?this.removeLabel(e):this.kill(null,e)},u._remove=function(t,i){e.prototype._remove.call(this,t,i);var s=this._last;return s?this._time>s._startTime+s._totalDuration/s._timeScale&&(this._time=this.duration(),this._totalTime=this._totalDuration):this._time=this._totalTime=0,this},u.append=function(t,e){return this.add(t,this._parseTimeOrLabel(null,e,!0,t))},u.insert=u.insertMultiple=function(t,e,i,s){return this.add(t,e||0,i,s)},u.appendMultiple=function(t,e,i,s){return this.add(t,this._parseTimeOrLabel(null,e,!0,t),i,s)},u.addLabel=function(t,e){return this._labels[t]=this._parseTimeOrLabel(e),this},u.addPause=function(t,e,i,s){return this.call(l,["{self}",e,i,s],this,t)},u.removeLabel=function(t){return delete this._labels[t],this},u.getLabelTime=function(t){return null!=this._labels[t]?this._labels[t]:-1},u._parseTimeOrLabel=function(e,i,s,r){var n;if(r instanceof t&&r.timeline===this)this.remove(r);else if(r&&(r instanceof Array||r.push&&a(r)))for(n=r.length;--n>-1;)r[n]instanceof t&&r[n].timeline===this&&this.remove(r[n]);if("string"==typeof i)return this._parseTimeOrLabel(i,s&&"number"==typeof e&&null==this._labels[i]?e-this.duration():0,s);if(i=i||0,"string"!=typeof e||!isNaN(e)&&null==this._labels[e])null==e&&(e=this.duration());else{if(n=e.indexOf("="),-1===n)return null==this._labels[e]?s?this._labels[e]=this.duration()+i:i:this._labels[e]+i;i=parseInt(e.charAt(n-1)+"1",10)*Number(e.substr(n+1)),e=n>1?this._parseTimeOrLabel(e.substr(0,n-1),0,s):this.duration()}return Number(e)+i},u.seek=function(t,e){return this.totalTime("number"==typeof t?t:this._parseTimeOrLabel(t),e!==!1)},u.stop=function(){return this.paused(!0)},u.gotoAndPlay=function(t,e){return this.play(t,e)},u.gotoAndStop=function(t,e){return this.pause(t,e)}
52508,u.render=function(t,e,i){this._gc&&this._enabled(!0,!1);var s,n,a,h,l,_=this._dirty?this.totalDuration():this._totalDuration,u=this._time,p=this._startTime,f=this._timeScale,c=this._paused;if(t>=_?(this._totalTime=this._time=_,this._reversed||this._hasPausedChild()||(n=!0,h="onComplete",0===this._duration&&(0===t||0>this._rawPrevTime||this._rawPrevTime===r)&&this._rawPrevTime!==t&&this._first&&(l=!0,this._rawPrevTime>r&&(h="onReverseComplete"))),this._rawPrevTime=this._duration||!e||t?t:r,t=_+1e-6):1e-7>t?(this._totalTime=this._time=0,(0!==u||0===this._duration&&(this._rawPrevTime>r||0>t&&this._rawPrevTime>=0))&&(h="onReverseComplete",n=this._reversed),0>t?(this._active=!1,0===this._duration&&this._rawPrevTime>=0&&this._first&&(l=!0),this._rawPrevTime=t):(this._rawPrevTime=this._duration||!e||t?t:r,t=0,this._initted||(l=!0))):this._totalTime=this._time=this._rawPrevTime=t,this._time!==u&&this._first||i||l){if(this._initted||(this._initted=!0),this._active||!this._paused&&this._time!==u&&t>0&&(this._active=!0),0===u&&this.vars.onStart&&0!==this._time&&(e||this.vars.onStart.apply(this.vars.onStartScope||this,this.vars.onStartParams||o)),this._time>=u)for(s=this._first;s&&(a=s._next,!this._paused||c);)(s._active||s._startTime<=this._time&&!s._paused&&!s._gc)&&(s._reversed?s.render((s._dirty?s.totalDuration():s._totalDuration)-(t-s._startTime)*s._timeScale,e,i):s.render((t-s._startTime)*s._timeScale,e,i)),s=a;else for(s=this._last;s&&(a=s._prev,!this._paused||c);)(s._active||u>=s._startTime&&!s._paused&&!s._gc)&&(s._reversed?s.render((s._dirty?s.totalDuration():s._totalDuration)-(t-s._startTime)*s._timeScale,e,i):s.render((t-s._startTime)*s._timeScale,e,i)),s=a;this._onUpdate&&(e||this._onUpdate.apply(this.vars.onUpdateScope||this,this.vars.onUpdateParams||o)),h&&(this._gc||(p===this._startTime||f!==this._timeScale)&&(0===this._time||_>=this.totalDuration())&&(n&&(this._timeline.autoRemoveChildren&&this._enabled(!1,!1),this._active=!1),!e&&this.vars[h]&&this.vars[h].apply(this.vars[h+"Scope"]||this,this.vars[h+"Params"]||o)))}},u._hasPausedChild=function(){for(var t=this._first;t;){if(t._paused||t instanceof s&&t._hasPausedChild())return!0;t=t._next}return!1},u.getChildren=function(t,e,s,r){r=r||-9999999999;for(var n=[],a=this._first,o=0;a;)r>a._startTime||(a instanceof i?e!==!1&&(n[o++]=a):(s!==!1&&(n[o++]=a),t!==!1&&(n=n.concat(a.getChildren(!0,e,s)),o=n.length))),a=a._next;return n},u.getTweensOf=function(t,e){for(var s=i.getTweensOf(t),r=s.length,n=[],a=0;--r>-1;)(s[r].timeline===this||e&&this._contains(s[r]))&&(n[a++]=s[r]);return n},u._contains=function(t){for(var e=t.timeline;e;){if(e===this)return!0;e=e.timeline}return!1},u.shiftChildren=function(t,e,i){i=i||0;for(var s,r=this._first,n=this._labels;r;)r._startTime>=i&&(r._startTime+=t),r=r._next;if(e)for(s in n)n[s]>=i&&(n[s]+=t);return this._uncache(!0)},u._kill=function(t,e){if(!t&&!e)return this._enabled(!1,!1);for(var i=e?this.getTweensOf(e):this.getChildren(!0,!0,!1),s=i.length,r=!1;--s>-1;)i[s]._kill(t,e)&&(r=!0);return r},u.clear=function(t){var e=this.getChildren(!1,!0,!0),i=e.length;for(this._time=this._totalTime=0;--i>-1;)e[i]._enabled(!1,!1);return t!==!1&&(this._labels={}),this._uncache(!0)},u.invalidate=function(){for(var t=this._first;t;)t.invalidate(),t=t._next;return this},u._enabled=function(t,i){if(t===this._gc)for(var s=this._first;s;)s._enabled(t,!0),s=s._next;return e.prototype._enabled.call(this,t,i)},u.duration=function(t){return arguments.length?(0!==this.duration()&&0!==t&&this.timeScale(this._duration/t),this):(this._dirty&&this.totalDuration(),this._duration)},u.totalDuration=function(t){if(!arguments.length){if(this._dirty){for(var e,i,s=0,r=this._last,n=999999999999;r;)e=r._prev,r._dirty&&r.totalDuration(),r._startTime>n&&this._sortChildren&&!r._paused?this.add(r,r._startTime-r._delay):n=r._startTime,0>r._startTime&&!r._paused&&(s-=r._startTime,this._timeline.smoothChildTiming&&(this._startTime+=r._startTime/this._timeScale),this.shiftChildren(-r._startTime,!1,-9999999999),n=0),i=r._startTime+r._totalDuration/r._timeScale,i>s&&(s=i),r=e;this._duration=this._totalDuration=s,this._dirty=!1}return this._totalDuration}return 0!==this.totalDuration()&&0!==t&&this.timeScale(this._totalDuration/t),this},u.usesFrames=function(){for(var e=this._timeline;e._timeline;)e=e._timeline;return e===t._rootFramesTimeline},u.rawTime=function(){return this._paused?this._totalTime:(this._timeline.rawTime()-this._startTime)*this._timeScale},s},!0),window._gsDefine("TimelineMax",["TimelineLite","TweenLite","easing.Ease"],function(t,e,
52508i){var s=function(e){t.call(this,e),this._repeat=this.vars.repeat||0,this._repeatDelay=this.vars.repeatDelay||0,this._cycle=0,this._yoyo=this.vars.yoyo===!0,this._dirty=!0},r=1e-10,n=[],a=new i(null,null,1,0),o=s.prototype=new t;return o.constructor=s,o.kill()._gc=!1,s.version="1.11.0",o.invalidate=function(){return this._yoyo=this.vars.yoyo===!0,this._repeat=this.vars.repeat||0,this._repeatDelay=this.vars.repeatDelay||0,this._uncache(!0),t.prototype.invalidate.call(this)},o.addCallback=function(t,i,s,r){return this.add(e.delayedCall(0,t,s,r),i)},o.removeCallback=function(t,e){if(t)if(null==e)this._kill(null,t);else for(var i=this.getTweensOf(t,!1),s=i.length,r=this._parseTimeOrLabel(e);--s>-1;)i[s]._startTime===r&&i[s]._enabled(!1,!1);return this},o.tweenTo=function(t,i){i=i||{};var s,r,o={ease:a,overwrite:2,useFrames:this.usesFrames(),immediateRender:!1};for(s in i)o[s]=i[s];return o.time=this._parseTimeOrLabel(t),r=new e(this,Math.abs(Number(o.time)-this._time)/this._timeScale||.001,o),o.onStart=function(){r.target.paused(!0),r.vars.time!==r.target.time()&&r.duration(Math.abs(r.vars.time-r.target.time())/r.target._timeScale),i.onStart&&i.onStart.apply(i.onStartScope||r,i.onStartParams||n)},r},o.tweenFromTo=function(t,e,i){i=i||{},t=this._parseTimeOrLabel(t),i.startAt={onComplete:this.seek,onCompleteParams:[t],onCompleteScope:this},i.immediateRender=i.immediateRender!==!1;var s=this.tweenTo(e,i);return s.duration(Math.abs(s.vars.time-t)/this._timeScale||.001)},o.render=function(t,e,i){this._gc&&this._enabled(!0,!1);var s,a,o,h,l,_,u=this._dirty?this.totalDuration():this._totalDuration,p=this._duration,f=this._time,c=this._totalTime,m=this._startTime,d=this._timeScale,g=this._rawPrevTime,v=this._paused,y=this._cycle;if(t>=u?(this._locked||(this._totalTime=u,this._cycle=this._repeat),this._reversed||this._hasPausedChild()||(a=!0,h="onComplete",0===this._duration&&(0===t||0>g||g===r)&&g!==t&&this._first&&(l=!0,g>r&&(h="onReverseComplete"))),this._rawPrevTime=this._duration||!e||t?t:r,this._yoyo&&0!==(1&this._cycle)?this._time=t=0:(this._time=p,t=p+1e-6)):1e-7>t?(this._locked||(this._totalTime=this._cycle=0),this._time=0,(0!==f||0===p&&(g>r||0>t&&g>=0)&&!this._locked)&&(h="onReverseComplete",a=this._reversed),0>t?(this._active=!1,0===p&&g>=0&&this._first&&(l=!0),this._rawPrevTime=t):(this._rawPrevTime=p||!e||t?t:r,t=0,this._initted||(l=!0))):(0===p&&0>g&&(l=!0),this._time=this._rawPrevTime=t,this._locked||(this._totalTime=t,0!==this._repeat&&(_=p+this._repeatDelay,this._cycle=this._totalTime/_>>0,0!==this._cycle&&this._cycle===this._totalTime/_&&this._cycle--,this._time=this._totalTime-this._cycle*_,this._yoyo&&0!==(1&this._cycle)&&(this._time=p-this._time),this._time>p?(this._time=p,t=p+1e-6):0>this._time?this._time=t=0:t=this._time))),this._cycle!==y&&!this._locked){var T=this._yoyo&&0!==(1&y),w=T===(this._yoyo&&0!==(1&this._cycle)),x=this._totalTime,b=this._cycle,P=this._rawPrevTime,S=this._time;if(this._totalTime=y*p,y>this._cycle?T=!T:this._totalTime+=p,this._time=f,this._rawPrevTime=0===p?g-1e-5:g,this._cycle=y,this._locked=!0,f=T?0:p,this.render(f,e,0===p),e||this._gc||this.vars.onRepeat&&this.vars.onRepeat.apply(this.vars.onRepeatScope||this,this.vars.onRepeatParams||n),w&&(f=T?p+1e-6:-1e-6,this.render(f,!0,!1)),this._locked=!1,this._paused&&!v)return;this._time=S,this._totalTime=x,this._cycle=b,this._rawPrevTime=P}if(!(this._time!==f&&this._first||i||l))return c!==this._totalTime&&this._onUpdate&&(e||this._onUpdate.apply(this.vars.onUpdateScope||this,this.vars.onUpdateParams||n)),void 0;if(this._initted||(this._initted=!0),this._active||!this._paused&&this._totalTime!==c&&t>0&&(this._active=!0),0===c&&this.vars.onStart&&0!==this._totalTime&&(e||this.vars.onStart.apply(this.vars.onStartScope||this,this.vars.onStartParams||n)),this._time>=f)for(s=this._first;s&&(o=s._next,!this._paused||v);)(s._active||s._startTime<=this._time&&!s._paused&&!s._gc)&&(s._reversed?s.render((s._dirty?s.totalDuration():s._totalDuration)-(t-s._startTime)*s._timeScale,e,i):s.render((t-s._startTime)*s._timeScale,e,i)),s=o;else for(s=this._last;s&&(o=s._prev,!this._paused||v);)(s._active||f>=s._startTime&&!s._paused&&!s._gc)&&(s._reversed?s.render((s._dirty?s.totalDuration():s._totalDuration)-(t-s._startTime)*s._timeScale,e,i):s.render((t-s._startTime)*s._timeScale,e,i)),s=o;
vendor: 10,448 bytes, lines 52508-52509
52508this._onUpdate&&(e||this._onUpdate.apply(this.vars.onUpdateScope||this,this.vars.onUpdateParams||n)),h&&(this._locked||this._gc||(m===this._startTime||d!==this._timeScale)&&(0===this._time||u>=this.totalDuration())&&(a&&(this._timeline.autoRemoveChildren&&this._enabled(!1,!1),this._active=!1),!e&&this.vars[h]&&this.vars[h].apply(this.vars[h+"Scope"]||this,this.vars[h+"Params"]||n)))},o.getActive=function(t,e,i){null==t&&(t=!0),null==e&&(e=!0),null==i&&(i=!1);var s,r,n=[],a=this.getChildren(t,e,i),o=0,h=a.length;for(s=0;h>s;s++)r=a[s],r.isActive()&&(n[o++]=r);return n},o.getLabelAfter=function(t){t||0!==t&&(t=this._time);var e,i=this.getLabelsArray(),s=i.length;for(e=0;s>e;e++)if(i[e].time>t)return i[e].name;return null},o.getLabelBefore=function(t){null==t&&(t=this._time);for(var e=this.getLabelsArray(),i=e.length;--i>-1;)if(t>e[i].time)return e[i].name;return null},o.getLabelsArray=function(){var t,e=[],i=0;for(t in this._labels)e[i++]={time:this._labels[t],name:t};return e.sort(function(t,e){return t.time-e.time}),e},o.progress=function(t){return arguments.length?this.totalTime(this.duration()*(this._yoyo&&0!==(1&this._cycle)?1-t:t)+this._cycle*(this._duration+this._repeatDelay),!1):this._time/this.duration()},o.totalProgress=function(t){return arguments.length?this.totalTime(this.totalDuration()*t,!1):this._totalTime/this.totalDuration()},o.totalDuration=function(e){return arguments.length?-1===this._repeat?this:this.duration((e-this._repeat*this._repeatDelay)/(this._repeat+1)):(this._dirty&&(t.prototype.totalDuration.call(this),this._totalDuration=-1===this._repeat?999999999999:this._duration*(this._repeat+1)+this._repeatDelay*this._repeat),this._totalDuration)},o.time=function(t,e){return arguments.length?(this._dirty&&this.totalDuration(),t>this._duration&&(t=this._duration),this._yoyo&&0!==(1&this._cycle)?t=this._duration-t+this._cycle*(this._duration+this._repeatDelay):0!==this._repeat&&(t+=this._cycle*(this._duration+this._repeatDelay)),this.totalTime(t,e)):this._time},o.repeat=function(t){return arguments.length?(this._repeat=t,this._uncache(!0)):this._repeat},o.repeatDelay=function(t){return arguments.length?(this._repeatDelay=t,this._uncache(!0)):this._repeatDelay},o.yoyo=function(t){return arguments.length?(this._yoyo=t,this):this._yoyo},o.currentLabel=function(t){return arguments.length?this.seek(t,!0):this.getLabelBefore(this._time+1e-8)},s},!0),function(){var t=180/Math.PI,e=[],i=[],s=[],r={},n=function(t,e,i,s){this.a=t,this.b=e,this.c=i,this.d=s,this.da=s-t,this.ca=i-t,this.ba=e-t},a=",x,y,z,left,top,right,bottom,marginTop,marginLeft,marginRight,marginBottom,paddingLeft,paddingTop,paddingRight,paddingBottom,backgroundPosition,backgroundPosition_y,",o=function(t,e,i,s){var r={a:t},n={},a={},o={c:s},h=(t+e)/2,l=(e+i)/2,_=(i+s)/2,u=(h+l)/2,p=(l+_)/2,f=(p-u)/8;return r.b=h+(t-h)/4,n.b=u+f,r.c=n.a=(r.b+n.b)/2,n.c=a.a=(u+p)/2,a.b=p-f,o.b=_+(s-_)/4,a.c=o.a=(a.b+o.b)/2,[r,n,a,o]},h=function(t,r,n,a,h){var l,_,u,p,f,c,m,d,g,v,y,T,w,x=t.length-1,b=0,P=t[0].a;for(l=0;x>l;l++)f=t[b],_=f.a,u=f.d,p=t[b+1].d,h?(y=e[l],T=i[l],w=.25*(T+y)*r/(a?.5:s[l]||.5),c=u-(u-_)*(a?.5*r:0!==y?w/y:0),m=u+(p-u)*(a?.5*r:0!==T?w/T:0),d=u-(c+((m-c)*(3*y/(y+T)+.5)/4||0))):(c=u-.5*(u-_)*r,m=u+.5*(p-u)*r,d=u-(c+m)/2),c+=d,m+=d,f.c=g=c,f.b=0!==l?P:P=f.a+.6*(f.c-f.a),f.da=u-_,f.ca=g-_,f.ba=P-_,n?(v=o(_,P,g,u),t.splice(b,1,v[0],v[1],v[2],v[3]),b+=4):b++,P=m;f=t[b],f.b=P,f.c=P+.4*(f.d-P),f.da=f.d-f.a,f.ca=f.c-f.a,f.ba=P-f.a,n&&(v=o(f.a,P,f.c,f.d),t.splice(b,1,v[0],v[1],v[2],v[3]))},l=function(t,s,r,a){var o,h,l,_,u,p,f=[];if(a)for(t=[a].concat(t),h=t.length;--h>-1;)"string"==typeof(p=t[h][s])&&"="===p.charAt(1)&&(t[h][s]=a[s]+Number(p.charAt(0)+p.substr(2)));if(o=t.length-2,0>o)return f[0]=new n(t[0][s],0,0,t[-1>o?0:1][s]),f;for(h=0;o>h;h++)l=t[h][s],_=t[h+1][s],f[h]=new n(l,0,0,_),r&&(u=t[h+2][s],e[h]=(e[h]||0)+(_-l)*(_-l),i[h]=(i[h]||0)+(u-_)*(u-_));return f[h]=new n(t[h][s],0,0,t[h+1][s]),f},_=function(t,n,o,_,u,p){var f,c,m,d,g,v,y,T,w={},x=[],b=p||t[0];u="string"==typeof u?","+u+",":a,null==n&&(n=1);for(c in t[0])x.push(c);if(t.length>1){for(T=t[t.length-1],y=!0,f=x.length;--f>-1;)if(c=x[f],Math.abs(b[c]-T[c])>.05){y=!1;break}y&&(t=t.concat(),p&&t.unshift(p),t.push(t[1]),p=t[t.length-3])}for(e.length=i.length=s.length=0,f=x.length;--f>-1;)c=x[f],r[c]=-1!==u.indexOf(","+c+","),w[c]=l(t,c,r[c],p);for(f=e.length;--f>-1;)e[f]=Math.sqrt(e[f]),i[f]=Math.sqrt(i[f]);if(!_){for(f=x.length;--f>-1;)if(r[c])for(m=w[x[f]],v=m.length-1,d=0;v>d;d++)g=m[d+1].da/i[d]+m[d].da/e[d],s[d]=(s[d]||0)+g*g;for(f=s.length;--f>-1;)s[f]=Math.sqrt(s[f])}for(f=x.length,d=o?4:1;--f>-1;)c=x[f],m=w[c],h(m,n,o,_,r[c]),y&&(m.splice(0,d),m.splice(m.length-d,d));return w},u=function(t,e,i){e=e||"soft";var s,r,a,o,h,l,_,u,p,f,c,m={},d="cubic"===e?3:2,g="soft"===e,v=[];if(g&&i&&(t=[i].concat(t)),null==t||d+1>t.length)throw"invalid Bezier data";for(p in t[0])v.push(p);for(l=v.length;--l>-1;){for(p=v[l],m[p]=h=[],f=0,u=t.length,_=0;u>_;_++)s=null==i?t[_][p]:"string"==typeof(c=t[_][p])&&"="===c.charAt(1)?i[p]+Number(c.charAt(0)+c.substr(2)):Number(c),g&&_>1&&u-1>_&&(h[f++]=(s+h[f-2])/2),h[f++]=s;for(u=f-d+1,f=0,_=0;u>_;_+=d)s=h[_],r=h[_+1],a=h[_+2],o=2===d?0:h[_+3],h[f++]=c=3===d?new n(s,r,a,o):new n(s,(2*r+s)/3,(2*r+a)/3,a);h.length=f}return m},p=function(t,e,i){for(var s,r,n,a,o,h,l,_,u,p,f,c=1/i,m=t.length;--m>-1;)for(p=t[m],n=p.a,a=p.d-n,o=p.c-n,h=p.b-n,s=r=0,_=1;i>=_;_++)l=c*_,u=1-l,s=r-(r=(l*l*a+3*u*(l*o+u*h))*l),f=m*i+_-1,e[f]=(e[f]||0)+s*s},f=function(t,e){e=e>>0||6;var i,s,r,n,a=[],o=[],h=0,l=0,_=e-1,u=[],f=[];for(i in t)p(t[i],a,e);for(r=a.length,s=0;r>s;s++)h+=Math.sqrt(a[s]),n=s%e,f[n]=h,n===_&&(l+=h,n=s/e>>0,u[n]=f,o[n]=l,h=0,f=[]);return{length:l,lengths:o,segments:u}},c=window._gsDefine.plugin({propName:"bezier",priority:-1,API:2,global:!0,init:function(t,e,i){this._target=t,e instanceof Array&&(e={values:e}),this._func={},this._round={},this._props=[],this._timeRes=null==e.timeResolution?6:parseInt(e.timeResolution,10);var s,r,n,a,o,h=e.values||[],l={},p=h[0],c=e.autoRotate||i.vars.orientToBezier;this._autoRotate=c?c instanceof Array?c:[["x","y","rotation",c===!0?0:Number(c)||0]]:null;for(s in p)this._props.push(s);for(n=this._props.length;--n>-1;)s=this._props[n],this._overwriteProps.push(s),r=this._func[s]="function"==typeof t[s],l[s]=r?t[s.indexOf("set")||"function"!=typeof t["get"+s.substr(3)]?s:"get"+s.substr(3)]():parseFloat(t[s]),o||l[s]!==h[0][s]&&(o=l);if(this._beziers="cubic"!==e.type&&"quadratic"!==e.type&&"soft"!==e.type?_(h,isNaN(e.curviness)?1:e.curviness,!1,"thruBasic"===e.type,e.correlate,o):u(h,e.type,l),this._segCount=this._beziers[s].length,this._timeRes){var m=f(this._beziers,this._timeRes);this._length=m.length,this._lengths=m.lengths,this._segments=m.segments,this._l1=this._li=this._s1=this._si=0,this._l2=this._lengths[0],this._curSeg=this._segments[0],this._s2=this._curSeg[0],this._prec=1/this._curSeg.length}if(c=this._autoRotate)for(c[0]instanceof Array||(this._autoRotate=c=[c]),n=c.length;--n>-1;)for(a=0;3>a;a++)s=c[n][a],this._func[s]="function"==typeof t[s]?t[s.indexOf("set")||"function"!=typeof t["get"+s.substr(3)]?s:"get"+s.substr(3)]:!1;return!0},set:function(e){var i,s,r,n,a,o,h,l,_,u,p=this._segCount,f=this._func,c=this._target;if(this._timeRes){if(_=this._lengths,u=this._curSeg,e*=this._length,r=this._li,e>this._l2&&p-1>r){for(l=p-1;l>r&&e>=(this._l2=_[++r]););this._l1=_[r-1],this._li=r,this._curSeg=u=this._segments[r],this._s2=u[this._s1=this._si=0]}else if(this._l1>e&&r>0){for(;r>0&&(this._l1=_[--r])>=e;);0===r&&this._l1>e?this._l1=0:r++,this._l2=_[r],this._li=r,this._curSeg=u=this._segments[r],this._s1=u[(this._si=u.length-1)-1]||0,this._s2=u[this._si]}if(i=r,e-=this._l1,r=this._si,e>this._s2&&u.length-1>r){for(l=u.length-1;l>r&&e>=(this._s2=u[++r]););this._s1=u[r-1],this._si=r}else if(this._s1>e&&r>0){for(;r>0&&(this._s1=u[--r])>=e;);0===r&&this._s1>e?this._s1=0:r++,this._s2=u[r],this._si=r}o=(r+(e-this._s1)/(this._s2-this._s1))*this._prec}else i=0>e?0:e>=1?p-1:p*e>>0,o=(e-i*(1/p))*p;for(s=1-o,r=this._props.length;--r>-1;)n=this._props[r],a=this._beziers[n][i],h=(o*o*a.da+3*s*(o*a.ca+s*a.ba))*o+a.a,this._round[n]&&(h=h+(h>0?.5:-.5)>>0),f[n]?c[n](h):c[n]=h;if(this._autoRotate){var m,d,g,v,y,T,w,x=this._autoRotate;for(r=x.length;--r>-1;)n=x[r][2],T=x[r][3]||0,w=x[r][4]===!0?1:t,a=this._beziers[x[r][0]],m=this._beziers[x[r][1]],a&&m&&(a=a[i],m=m[i],d=a.a+(a.b-a.a)*o,v=a.b+(a.c-a.b)*o,d+=(v-d)*o,v+=(a.c+(a.d-a.c)*o-v)*o,g=m.a+(m.b-m.a)*o,y=m.b+(m.c-m.b)*o,g+=(y-g)*o,y+=(m.c+(m.d-m.c)*o-y)*o,h=Math.atan2(y-g,v-d)*w+T,f[n]?c[n](h):c[n]=h)}}}),m=c.prototype;c.bezierThrough=_,c.cubicToQuadratic=o,c._autoCSS=!0,c.quadraticToCubic=function(t,e,i){return new n(t,(2*e+t)/3,(2*e+i)/3,i)},c._cssRegister=function(){var t=window._gsDefine.globals.CSSPlugin;if(t){var e=t._internals,i=e._parseToProxy,s=e._setPluginRatio,r=e.CSSPropTween;e._registerComplexSpecialProp("bezier",{parser:function(t,e,n,a,o,h){e instanceof Array&&(e={values:e}),h=new c;var l,_,u,p=e.values,f=p.length-1,m=[],d={};if(0>f)return o;for(l=0;f>=l;l++)u=i(t,p[l],a,o,h,f!==l),m[l]=u.end;for(_ in e)d[_]=e[_];return d.values=m,o=new r(t,"bezier",0,0,u.pt,2),o.data=u,o.plugin=h,o.setRatio=s,0===d.autoRotate&&(d.autoRotate=!0),!d.autoRotate||d.autoRotate instanceof Array||(l=d.autoRotate===!0?0:Number(d.autoRotate),d.autoRotate=null!=u.end.left?[["left","top","rotation",l,!1]]:null!=u.end.x?[["x","y","rotation",l,!1]]:!1),d.autoRotate&&(a._transform||a._enableTransforms(!1),u.autoRotate=a._target._gsTransform),h._onInitTween(u.proxy,d,a._tween),o}})}},m._roundProps=function(t,e){for(var i=this._overwriteProps,s=i.length;--s>-1;)(t[i[s]]||t.bezier||t.bezierThrough)&&(this._round[i[s]]=e)},m._kill=function(t){var e,i,s=this._props; 52509for(e in this._beziers)if(e in t)for(delete this._beziers[e],delete this._func[e],i=s.length;--i>-1;)s[i]===e&&s.splice(i,1);return this._super._kill.call(this,t)}}(),window._gsDefine("plugins.CSSPlugin",["plugins.TweenPlugin","TweenLite"],function(t,e){var i,s,r,n,a=function(){t.call(this,"css"),this._overwriteProps.length=0,this.setRatio=a.prototype.setRatio},o={},h=a.prototype=new t("css");h.constructor=a,a.version="1.11.2",a.API=2,a.defaultTransformPerspective=0,h="px",a.suffixMap={top:h,right:h,bottom:h,left:h,width:h,height:h,fontSize:h,padding:h,margin:h,perspective:h};var l,_,u,p,f,c,m=/(?:\d|\-\d|\.\d|\-\.\d)+/g,d=/(?:\d|\-\d|\.\d|\-\.\d|\+=\d|\-=\d|\+=.\d|\-=\.\d)+/g,g=/(?
52509:\+=|\-=|\-|\b)[\d\-\.]+[a-zA-Z0-9]*(?:%|\b)/gi,v=/[^\d\-\.]/g,y=/(?:\d|\-|\+|=|#|\.)*/g,T=/opacity *= *([^)]*)/,w=/opacity:([^;]*)/,x=/alpha\(opacity *=.+?\)/i,b=/^(rgb|hsl)/,P=/([A-Z])/g,S=/-([a-z])/gi,k=/(^(?:url\(\"|url\())|(?:(\"\))$|\)$)/gi,R=function(t,e){return e.toUpperCase()},A=/(?:Left|Right|Width)/i,C=/(M11|M12|M21|M22)=[\d\-\.e]+/gi,O=/progid\:DXImageTransform\.Microsoft\.Matrix\(.+?\)/i,D=/,(?=[^\)]*(?:\(|$))/gi,M=Math.PI/180,I=180/Math.PI,E={},F=document,N=F.createElement("div"),L=F.createElement("img"),X=a._internals={_specialProps:o},z=navigator.userAgent,U=function(){var t,e=z.indexOf("Android"),i=F.createElement("div");return u=-1!==z.indexOf("Safari")&&-1===z.indexOf("Chrome")&&(-1===e||Number(z.substr(e+8,1))>3),f=u&&6>Number(z.substr(z.indexOf("Version/")+8,1)),p=-1!==z.indexOf("Firefox"),/MSIE ([0-9]{1,}[\.0-9]{0,})/.exec(z)&&(c=parseFloat(RegExp.$1)),i.innerHTML="<a style='top:1px;opacity:.55;'>a</a>",t=i.getElementsByTagName("a")[0],t?/^0.55/.test(t.style.opacity):!1}(),Y=function(t){return T.test("string"==typeof t?t:(t.currentStyle?t.currentStyle.filter:t.style.filter)||"")?parseFloat(RegExp.$1)/100:1},j=function(t){window.console&&console.log(t)},B="",q="",V=function(t,e){e=e||N;var i,s,r=e.style;if(void 0!==r[t])return t;for(t=t.charAt(0).toUpperCase()+t.substr(1),i=["O","Moz","ms","Ms","Webkit"],s=5;--s>-1&&void 0===r[i[s]+t];);return s>=0?(q=3===s?"ms":i[s],B="-"+q.toLowerCase()+"-",q+t):null},Z=F.defaultView?F.defaultView.getComputedStyle:function(){},G=a.getStyle=function(t,e,i,s,r){var n;return U||"opacity"!==e?(!s&&t.style[e]?n=t.style[e]:(i=i||Z(t,null))?(t=i.getPropertyValue(e.replace(P,"-$1").toLowerCase()),n=t||i.length?t:i[e]):t.currentStyle&&(n=t.currentStyle[e]),null==r||n&&"none"!==n&&"auto"!==n&&"auto auto"!==n?n:r):Y(t)},$=function(t,e,i,s,r){if("px"===s||!s)return i;if("auto"===s||!i)return 0;var n,a=A.test(e),o=t,h=N.style,l=0>i;return l&&(i=-i),"%"===s&&-1!==e.indexOf("border")?n=i/100*(a?t.clientWidth:t.clientHeight):(h.cssText="border:0 solid red;position:"+G(t,"position")+";line-height:0;","%"!==s&&o.appendChild?h[a?"borderLeftWidth":"borderTopWidth"]=i+s:(o=t.parentNode||F.body,h[a?"width":"height"]=i+s),o.appendChild(N),n=parseFloat(N[a?"offsetWidth":"offsetHeight"]),o.removeChild(N),0!==n||r||(n=$(t,e,i,s,!0))),l?-n:n},Q=function(t,e,i){if("absolute"!==G(t,"position",i))return 0;var s="left"===e?"Left":"Top",r=G(t,"margin"+s,i);return t["offset"+s]-($(t,e,parseFloat(r),r.replace(y,""))||0)},W=function(t,e){var i,s,r={};if(e=e||Z(t,null))if(i=e.length)for(;--i>-1;)r[e[i].replace(S,R)]=e.getPropertyValue(e[i]);else for(i in e)r[i]=e[i];else if(e=t.currentStyle||t.style)for(i in e)"string"==typeof i&&void 0!==r[i]&&(r[i.replace(S,R)]=e[i]);return U||(r.opacity=Y(t)),s=be(t,e,!1),r.rotation=s.rotation,r.skewX=s.skewX,r.scaleX=s.scaleX,r.scaleY=s.scaleY,r.x=s.x,r.y=s.y,xe&&(r.z=s.z,r.rotationX=s.rotationX,r.rotationY=s.rotationY,r.scaleZ=s.scaleZ),r.filters&&delete r.filters,r},H=function(t,e,i,s,r){var n,a,o,h={},l=t.style;for(a in i)"cssText"!==a&&"length"!==a&&isNaN(a)&&(e[a]!==(n=i[a])||r&&r[a])&&-1===a.indexOf("Origin")&&("number"==typeof n||"string"==typeof n)&&(h[a]="auto"!==n||"left"!==a&&"top"!==a?""!==n&&"auto"!==n&&"none"!==n||"string"!=typeof e[a]||""===e[a].replace(v,"")?n:0:Q(t,a),void 0!==l[a]&&(o=new ue(l,a,l[a],o)));if(s)for(a in s)"className"!==a&&(h[a]=s[a]);return{difs:h,firstMPT:o}},K={width:["Left","Right"],height:["Top","Bottom"]},J=["marginLeft","marginRight","marginTop","marginBottom"],te=function(t,e,i){var s=parseFloat("width"===e?t.offsetWidth:t.offsetHeight),r=K[e],n=r.length;for(i=i||Z(t,null);--n>-1;)s-=parseFloat(G(t,"padding"+r[n],i,!0))||0,s-=parseFloat(G(t,"border"+r[n]+"Width",i,!0))||0;return s},ee=function(t,e){(null==t||""===t||"auto"===t||"auto auto"===t)&&(t="0 0");var i=t.split(" "),s=-1!==t.indexOf("left")?"0%":-1!==t.indexOf("right")?"100%":i[0],r=-1!==t.indexOf("top")?"0%":-1!==t.indexOf("bottom")?"100%":i[1];return null==r?r="0":"center"===r&&(r="50%"),("center"===s||isNaN(parseFloat(s))&&-1===(s+"").indexOf("="))&&(s="50%"),e&&(e.oxp=-1!==s.indexOf("%"),e.oyp=-1!==r.indexOf("%"),e.oxr="="===s.charAt(1),e.oyr="="===r.charAt(1),e.ox=parseFloat(s.replace(v,"")),e.oy=parseFloat(r.replace(v,""))),s+" "+r+(i.length>2?" "+i[2]:"")},ie=function(t,e){return"string"==typeof t&&"="===t.charAt(1)?parseInt(t.charAt(0)+"1",10)*parseFloat(t.substr(2)):parseFloat(t)-parseFloat(e)},se=function(t,e){return null==t?e:"string"==typeof t&&"="===t.charAt(1)?parseInt(t.charAt(0)+"1",10)*Number(t.substr(2))+e:parseFloat(t)}
52509,re=function(t,e,i,s){var r,n,a,o,h=1e-6;return null==t?o=e:"number"==typeof t?o=t:(r=360,n=t.split("_"),a=Number(n[0].replace(v,""))*(-1===t.indexOf("rad")?1:I)-("="===t.charAt(1)?0:e),n.length&&(s&&(s[i]=e+a),-1!==t.indexOf("short")&&(a%=r,a!==a%(r/2)&&(a=0>a?a+r:a-r)),-1!==t.indexOf("_cw")&&0>a?a=(a+9999999999*r)%r-(0|a/r)*r:-1!==t.indexOf("ccw")&&a>0&&(a=(a-9999999999*r)%r-(0|a/r)*r)),o=e+a),h>o&&o>-h&&(o=0),o},ne={aqua:[0,255,255],lime:[0,255,0],silver:[192,192,192],black:[0,0,0],maroon:[128,0,0],teal:[0,128,128],blue:[0,0,255],navy:[0,0,128],white:[255,255,255],fuchsia:[255,0,255],olive:[128,128,0],yellow:[255,255,0],orange:[255,165,0],gray:[128,128,128],purple:[128,0,128],green:[0,128,0],red:[255,0,0],pink:[255,192,203],cyan:[0,255,255],transparent:[255,255,255,0]},ae=function(t,e,i){return t=0>t?t+1:t>1?t-1:t,0|255*(1>6*t?e+6*(i-e)*t:.5>t?i:2>3*t?e+6*(i-e)*(2/3-t):e)+.5},oe=function(t){var e,i,s,r,n,a;return t&&""!==t?"number"==typeof t?[t>>16,255&t>>8,255&t]:(","===t.charAt(t.length-1)&&(t=t.substr(0,t.length-1)),ne[t]?ne[t]:"#"===t.charAt(0)?(4===t.length&&(e=t.charAt(1),i=t.charAt(2),s=t.charAt(3),t="#"+e+e+i+i+s+s),t=parseInt(t.substr(1),16),[t>>16,255&t>>8,255&t]):"hsl"===t.substr(0,3)?(t=t.match(m),r=Number(t[0])%360/360,n=Number(t[1])/100,a=Number(t[2])/100,i=.5>=a?a*(n+1):a+n-a*n,e=2*a-i,t.length>3&&(t[3]=Number(t[3])),t[0]=ae(r+1/3,e,i),t[1]=ae(r,e,i),t[2]=ae(r-1/3,e,i),t):(t=t.match(m)||ne.transparent,t[0]=Number(t[0]),t[1]=Number(t[1]),t[2]=Number(t[2]),t.length>3&&(t[3]=Number(t[3])),t)):ne.black},he="(?:\\b(?:(?:rgb|rgba|hsl|hsla)\\(.+?\\))|\\B#.+?\\b";for(h in ne)he+="|"+h+"\\b";he=RegExp(he+")","gi");var le=function(t,e,i,s){if(null==t)return function(t){return t};var r,n=e?(t.match(he)||[""])[0]:"",a=t.split(n).join("").match(g)||[],o=t.substr(0,t.indexOf(a[0])),h=")"===t.charAt(t.length-1)?")":"",l=-1!==t.indexOf(" ")?" ":",",_=a.length,u=_>0?a[0].replace(m,""):"";return _?r=e?function(t){var e,p,f,c;if("number"==typeof t)t+=u;else if(s&&D.test(t)){for(c=t.replace(D,"|").split("|"),f=0;c.length>f;f++)c[f]=r(c[f]);return c.join(",")}if(e=(t.match(he)||[n])[0],p=t.split(e).join("").match(g)||[],f=p.length,_>f--)for(;_>++f;)p[f]=i?p[0|(f-1)/2]:a[f];return o+p.join(l)+l+e+h+(-1!==t.indexOf("inset")?" inset":"")}:function(t){var e,n,p;if("number"==typeof t)t+=u;else if(s&&D.test(t)){for(n=t.replace(D,"|").split("|"),p=0;n.length>p;p++)n[p]=r(n[p]);return n.join(",")}if(e=t.match(g)||[],p=e.length,_>p--)for(;_>++p;)e[p]=i?e[0|(p-1)/2]:a[p];return o+e.join(l)+h}:function(t){return t}},_e=function(t){return t=t.split(","),function(e,i,s,r,n,a,o){var h,l=(i+"").split(" ");for(o={},h=0;4>h;h++)o[t[h]]=l[h]=l[h]||l[(h-1)/2>>0];return r.parse(e,o,n,a)}},ue=(X._setPluginRatio=function(t){this.plugin.setRatio(t);for(var e,i,s,r,n=this.data,a=n.proxy,o=n.firstMPT,h=1e-6;o;)e=a[o.v],o.r?e=e>0?0|e+.5:0|e-.5:h>e&&e>-h&&(e=0),o.t[o.p]=e,o=o._next;if(n.autoRotate&&(n.autoRotate.rotation=a.rotation),1===t)for(o=n.firstMPT;o;){if(i=o.t,i.type){if(1===i.type){for(r=i.xs0+i.s+i.xs1,s=1;i.l>s;s++)r+=i["xn"+s]+i["xs"+(s+1)];i.e=r}}else i.e=i.s+i.xs0;o=o._next}},function(t,e,i,s,r){this.t=t,this.p=e,this.v=i,this.r=r,s&&(s._prev=this,this._next=s)}),pe=(X._parseToProxy=function(t,e,i,s,r,n){var a,o,h,l,_,u=s,p={},f={},c=i._transform,m=E;for(i._transform=null,E=e,s=_=i.parse(t,e,s,r),E=m,n&&(i._transform=c,u&&(u._prev=null,u._prev&&(u._prev._next=null)));s&&s!==u;){if(1>=s.type&&(o=s.p,f[o]=s.s+s.c,p[o]=s.s,n||(l=new ue(s,"s",o,l,s.r),s.c=0),1===s.type))for(a=s.l;--a>0;)h="xn"+a,o=s.p+"_"+h,f[o]=s.data[h],p[o]=s[h],n||(l=new ue(s,h,o,l,s.rxp[h]));s=s._next}return{proxy:p,end:f,firstMPT:l,pt:_}},X.CSSPropTween=function(t,e,s,r,a,o,h,l,_,u,p){this.t=t,this.p=e,this.s=s,this.c=r,this.n=h||e,t instanceof pe||n.push(this.n),this.r=l,this.type=o||0,_&&(this.pr=_,i=!0),this.b=void 0===u?s:u,this.e=void 0===p?s+r:p,a&&(this._next=a,a._prev=this)}),fe=a.parseComplex=function(t,e,i,s,r,n,a,o,h,_){i=i||n||"",a=new pe(t,e,0,0,a,_?2:1,null,!1,o,i,s),s+="";var u,p,f,c,g,v,y,T,w,x,P,S,k=i.split(", ").join(",").split(" "),R=s.split(", ").join(",").split(" "),A=k.length,C=l!==!1;for((-1!==s.indexOf(",")||-1!==i.indexOf(","))&&(k=k.join(" ").replace(D,", ").split(" "),R=R.join(" ").replace(D,", ").split(" "),A=k.length),A!==R.length&&(k=(n||"").split(" "),A=k.length),a.plugin=h,a.setRatio=_,u=0;A>u;u++)if(c=k[u],g=R[u],T=parseFloat(c),T||0===T)a.appendXtra("",T,ie(g,T),g.replace(d,""),C&&-1!==g.indexOf("px"),!0);else if(r&&("#"===c.charAt(0)||ne[c]||b.test(c)))S=","===g.charAt(g.length-1)?"),":")",c=oe(c),g=oe(g),w=c.length+g.length>
525096,w&&!U&&0===g[3]?(a["xs"+a.l]+=a.l?" transparent":"transparent",a.e=a.e.split(R[u]).join("transparent")):(U||(w=!1),a.appendXtra(w?"rgba(":"rgb(",c[0],g[0]-c[0],",",!0,!0).appendXtra("",c[1],g[1]-c[1],",",!0).appendXtra("",c[2],g[2]-c[2],w?",":S,!0),w&&(c=4>c.length?1:c[3],a.appendXtra("",c,(4>g.length?1:g[3])-c,S,!1)));else if(v=c.match(m)){if(y=g.match(d),!y||y.length!==v.length)return a;for(f=0,p=0;v.length>p;p++)P=v[p],x=c.indexOf(P,f),a.appendXtra(c.substr(f,x-f),Number(P),ie(y[p],P),"",C&&"px"===c.substr(x+P.length,2),0===p),f=x+P.length;a["xs"+a.l]+=c.substr(f)}else a["xs"+a.l]+=a.l?" "+c:c;if(-1!==s.indexOf("=")&&a.data){for(S=a.xs0+a.data.s,u=1;a.l>u;u++)S+=a["xs"+u]+a.data["xn"+u];a.e=S+a["xs"+u]}return a.l||(a.type=-1,a.xs0=a.e),a.xfirst||a},ce=9;for(h=pe.prototype,h.l=h.pr=0;--ce>0;)h["xn"+ce]=0,h["xs"+ce]="";h.xs0="",h._next=h._prev=h.xfirst=h.data=h.plugin=h.setRatio=h.rxp=null,h.appendXtra=function(t,e,i,s,r,n){var a=this,o=a.l;return a["xs"+o]+=n&&o?" "+t:t||"",i||0===o||a.plugin?(a.l++,a.type=a.setRatio?2:1,a["xs"+a.l]=s||"",o>0?(a.data["xn"+o]=e+i,a.rxp["xn"+o]=r,a["xn"+o]=e,a.plugin||(a.xfirst=new pe(a,"xn"+o,e,i,a.xfirst||a,0,a.n,r,a.pr),a.xfirst.xs0=0),a):(a.data={s:e+i},a.rxp={},a.s=e,a.c=i,a.r=r,a)):(a["xs"+o]+=e+(s||""),a)};var me=function(t,e){e=e||{},this.p=e.prefix?V(t)||t:t,o[t]=o[this.p]=this,this.format=e.formatter||le(e.defaultValue,e.color,e.collapsible,e.multi),e.parser&&(this.parse=e.parser),this.clrs=e.color,this.multi=e.multi,this.keyword=e.keyword,this.dflt=e.defaultValue,this.pr=e.priority||0},de=X._registerComplexSpecialProp=function(t,e,i){"object"!=typeof e&&(e={parser:i});var s,r,n=t.split(","),a=e.defaultValue;for(i=i||[a],s=0;n.length>s;s++)e.prefix=0===s&&e.prefix,e.defaultValue=i[s]||a,r=new me(n[s],e)},ge=function(t){if(!o[t]){var e=t.charAt(0).toUpperCase()+t.substr(1)+"Plugin";de(t,{parser:function(t,i,s,r,n,a,h){var l=(window.GreenSockGlobals||window).com.greensock.plugins[e];return l?(l._cssRegister(),o[s].parse(t,i,s,r,n,a,h)):(j("Error: "+e+" js file not loaded."),n)}})}};h=me.prototype,h.parseComplex=function(t,e,i,s,r,n){var a,o,h,l,_,u,p=this.keyword;if(this.multi&&(D.test(i)||D.test(e)?(o=e.replace(D,"|").split("|"),h=i.replace(D,"|").split("|")):p&&(o=[e],h=[i])),h){for(l=h.length>o.length?h.length:o.length,a=0;l>a;a++)e=o[a]=o[a]||this.dflt,i=h[a]=h[a]||this.dflt,p&&(_=e.indexOf(p),u=i.indexOf(p),_!==u&&(i=-1===u?h:o,i[a]+=" "+p));e=o.join(", "),i=h.join(", ")}return fe(t,this.p,e,i,this.clrs,this.dflt,s,this.pr,r,n)},h.parse=function(t,e,i,s,n,a){return this.parseComplex(t.style,this.format(G(t,this.p,r,!1,this.dflt)),this.format(e),n,a)},a.registerSpecialProp=function(t,e,i){de(t,{parser:function(t,s,r,n,a,o){var h=new pe(t,r,0,0,a,2,r,!1,i);return h.plugin=o,h.setRatio=e(t,s,n._tween,r),h},priority:i})};var ve="scaleX,scaleY,scaleZ,x,y,z,skewX,rotation,rotationX,rotationY,perspective".split(","),ye=V("transform"),Te=B+"transform",we=V("transformOrigin"),xe=null!==V("perspective"),be=function(t,e,i,s){if(t._gsTransform&&i&&!s)return t._gsTransform;var r,n,o,h,l,_,u,p,f,c,m,d,g,v=i?t._gsTransform||{skewY:0}:{skewY:0},y=0>v.scaleX,T=2e-5,w=1e5,x=179.99,b=x*M,P=xe?parseFloat(G(t,we,e,!1,"0 0 0").split(" ")[2])||v.zOrigin||0:0;for(ye?r=G(t,Te,e,!0):t.currentStyle&&(r=t.currentStyle.filter.match(C),r=r&&4===r.length?[r[0].substr(4),Number(r[2].substr(4)),Number(r[1].substr(4)),r[3].substr(4),v.x||0,v.y||0].join(","):""),n=(r||"").match(/(?:\-|\b)[\d\-\.e]+\b/gi)||[],o=n.length;--o>-1;)h=Number(n[o]),n[o]=(l=h-(h|=0))?(0|l*w+(0>l?-.5:.5))/w+h:h;if(16===n.length){var S=n[8],k=n[9],R=n[10],A=n[12],O=n[13],D=n[14];if(v.zOrigin&&(D=-v.zOrigin,A=S*D-n[12],O=k*D-n[13],D=R*D+v.zOrigin-n[14]),!i||s||null==v.rotationX){var E,F,N,L,X,z,U,Y=n[0],j=n[1],B=n[2],q=n[3],V=n[4],Z=n[5],$=n[6],Q=n[7],W=n[11],H=Math.atan2($,R),K=-b>H||H>b;v.rotationX=H*I,H&&(L=Math.cos(-H),X=Math.sin(-H),E=V*L+S*X,F=Z*L+k*X,N=$*L+R*X,S=V*-X+S*L,k=Z*-X+k*L,R=$*-X+R*L,W=Q*-X+W*L,V=E,Z=F,$=N),H=Math.atan2(S,Y),v.rotationY=H*I,H&&(z=-b>H||H>b,L=Math.cos(-H),X=Math.sin(-H),E=Y*L-S*X,F=j*L-k*X,N=B*L-R*X,k=j*X+k*L,R=B
52509*X+R*L,W=q*X+W*L,Y=E,j=F,B=N),H=Math.atan2(j,Z),v.rotation=H*I,H&&(U=-b>H||H>b,L=Math.cos(-H),X=Math.sin(-H),Y=Y*L+V*X,F=j*L+Z*X,Z=j*-X+Z*L,$=B*-X+$*L,j=F),U&&K?v.rotation=v.rotationX=0:U&&z?v.rotation=v.rotationY=0:z&&K&&(v.rotationY=v.rotationX=0),v.scaleX=(0|Math.sqrt(Y*Y+j*j)*w+.5)/w,v.scaleY=(0|Math.sqrt(Z*Z+k*k)*w+.5)/w,v.scaleZ=(0|Math.sqrt($*$+R*R)*w+.5)/w,v.skewX=0,v.perspective=W?1/(0>W?-W:W):0,v.x=A,v.y=O,v.z=D}}else if(!(xe&&!s&&n.length&&v.x===n[4]&&v.y===n[5]&&(v.rotationX||v.rotationY)||void 0!==v.x&&"none"===G(t,"display",e))){var J=n.length>=6,te=J?n[0]:1,ee=n[1]||0,ie=n[2]||0,se=J?n[3]:1;v.x=n[4]||0,v.y=n[5]||0,_=Math.sqrt(te*te+ee*ee),u=Math.sqrt(se*se+ie*ie),p=te||ee?Math.atan2(ee,te)*I:v.rotation||0,f=ie||se?Math.atan2(ie,se)*I+p:v.skewX||0,c=_-Math.abs(v.scaleX||0),m=u-Math.abs(v.scaleY||0),Math.abs(f)>90&&270>Math.abs(f)&&(y?(_*=-1,f+=0>=p?180:-180,p+=0>=p?180:-180):(u*=-1,f+=0>=f?180:-180)),d=(p-v.rotation)%180,g=(f-v.skewX)%180,(void 0===v.skewX||c>T||-T>c||m>T||-T>m||d>-x&&x>d&&false|d*w||g>-x&&x>g&&false|g*w)&&(v.scaleX=_,v.scaleY=u,v.rotation=p,v.skewX=f),xe&&(v.rotationX=v.rotationY=v.z=0,v.perspective=parseFloat(a.defaultTransformPerspective)||0,v.scaleZ=1)}v.zOrigin=P;for(o in v)T>v[o]&&v[o]>-T&&(v[o]=0);return i&&(t._gsTransform=v),v},Pe=function(t){var e,i,s=this.data,r=-s.rotation*M,n=r+s.skewX*M,a=1e5,o=(0|Math.cos(r)*s.scaleX*a)/a,h=(0|Math.sin(r)*s.scaleX*a)/a,l=(0|Math.sin(n)*-s.scaleY*a)/a,_=(0|Math.cos(n)*s.scaleY*a)/a,u=this.t.style,p=this.t.currentStyle;if(p){i=h,h=-l,l=-i,e=p.filter,u.filter="";var f,m,d=this.t.offsetWidth,g=this.t.offsetHeight,v="absolute"!==p.position,w="progid:DXImageTransform.Microsoft.Matrix(M11="+o+", M12="+h+", M21="+l+", M22="+_,x=s.x,b=s.y;if(null!=s.ox&&(f=(s.oxp?.01*d*s.ox:s.ox)-d/2,m=(s.oyp?.01*g*s.oy:s.oy)-g/2,x+=f-(f*o+m*h),b+=m-(f*l+m*_)),v?(f=d/2,m=g/2,w+=", Dx="+(f-(f*o+m*h)+x)+", Dy="+(m-(f*l+m*_)+b)+")"):w+=", sizingMethod='auto expand')",u.filter=-1!==e.indexOf("DXImageTransform.Microsoft.Matrix(")?e.replace(O,w):w+" "+e,(0===t||1===t)&&1===o&&0===h&&0===l&&1===_&&(v&&-1===w.indexOf("Dx=0, Dy=0")||T.test(e)&&100!==parseFloat(RegExp.$1)||-1===e.indexOf("gradient("&&e.indexOf("Alpha"))&&u.removeAttribute("filter")),!v){var P,S,k,R=8>c?1:-1;for(f=s.ieOffsetX||0,m=s.ieOffsetY||0,s.ieOffsetX=Math.round((d-((0>o?-o:o)*d+(0>h?-h:h)*g))/2+x),s.ieOffsetY=Math.round((g-((0>_?-_:_)*g+(0>l?-l:l)*d))/2+b),ce=0;4>ce;ce++)S=J[ce],P=p[S],i=-1!==P.indexOf("px")?parseFloat(P):$(this.t,S,parseFloat(P),P.replace(y,""))||0,k=i!==s[S]?2>ce?-s.ieOffsetX:-s.ieOffsetY:2>ce?f-s.ieOffsetX:m-s.ieOffsetY,u[S]=(s[S]=Math.round(i-k*(0===ce||2===ce?1:R)))+"px"}}},Se=function(){var t,e,i,s,r,n,a,o,h,l,_,u,f,c,m,d,g,v,y,T,w,x,b,P=this.data,S=this.t.style,k=P.rotation*M,R=P.scaleX,A=P.scaleY,C=P.scaleZ,O=P.perspective;if(p){var D=1e-4;D>R&&R>-D&&(R=C=2e-5),D>A&&A>-D&&(A=C=2e-5),!O||P.z||P.rotationX||P.rotationY||(O=0)}if(k||P.skewX)v=Math.cos(k),y=Math.sin(k),t=v,r=y,P.skewX&&(k-=P.skewX*M,v=Math.cos(k),y=Math.sin(k)),e=-y,n=v;else{if(!(P.rotationY||P.rotationX||1!==C||O))return S[ye]="translate3d("+P.x+"px,"+P.y+"px,"+P.z+"px)"+(1!==R||1!==A?" scale("+R+","+A+")":""),void 0;t=n=1,e=r=0}_=1,i=s=a=o=h=l=u=f=c=0,m=O?-1/O:0,d=P.zOrigin,g=1e5,k=P.rotationY*M,k&&(v=Math.cos(k),y=Math.sin(k),h=_*-y,f=m*-y,i=t*y,a=r*y,_*=v,m*=v,t*=v,r*=v),k=P.rotationX*M,k&&(v=Math.cos(k),y=Math.sin(k),T=e*v+i*y,w=n*v+a*y,x=l*v+_*y,b=c*v+m*y,i=e*-y+i*v,a=n*-y+a*v,_=l*-y+_*v,m=c*-y+m*v,e=T,n=w,l=x,c=b),1!==C&&(i*=C,a*=C,_*=C,m*=C),1!==A&&(e*=A,n*=A,l*=A,c*=A),1!==R&&(t*=R,r*=R,h*=R,f*=R),d&&(u-=d,s=i*u,o=a*u,u=_*u+d),s=(T=(s+=P.x)-(s|=0))?(0|T*g+(0>T?-.5:.5))/g+s:s,o=(T=(o+=P.y)-(o|=0))?(0|T*g+(0>T?-.5:.5))/g+o:o,u=(T=(u+=P.z)-(u|=0))?(0|T*g+(0>T?-.5:.5))/g+u:u,S[ye]="matrix3d("+[(0|t*g)/g,(0|r*g)/g,(0|h*g)/g,(0|f*g)/g,(0|e*g)/g,(0|n*g)/g,(0|l*g)/g,(0|c*g)/g,(0|i*g)/g,(0|a*g)/g,(0|_*g)/g,(0|m*g)/g,s,o,u,O?1+-u/O:1].join(",")+")"},ke=function(){var t,e,i,s,r,n,a,o,h,l=this.data,_=this.t,u=_.style;p&&(t=u.top?"top":u.bottom?"bottom":parseFloat(G(_,"top",null,!1))?"bottom":"top",e=G(_,t,null,!1),i=parseFloat(e)||0,s=e.substr((i+"").length)||"px",l._ffFix=!l._ffFix,u[t]=(l._ffFix?i+.05:i-.05)+s),l.rotation||l.skewX?(r=l.rotation*M,n=r-l.skewX*M,a=1e5,o=l.scaleX*a,h=l.scaleY*a,u[ye]="matrix("+(0|Math.cos(r)*o)
52509/a+","+(0|Math.sin(r)*o)/a+","+(0|Math.sin(n)*-h)/a+","+(0|Math.cos(n)*h)/a+","+l.x+","+l.y+")"):u[ye]="matrix("+l.scaleX+",0,0,"+l.scaleY+","+l.x+","+l.y+")"};de("transform,scale,scaleX,scaleY,scaleZ,x,y,z,rotation,rotationX,rotationY,rotationZ,skewX,skewY,shortRotation,shortRotationX,shortRotationY,shortRotationZ,transformOrigin,transformPerspective,directionalRotation,parseTransform,force3D",{parser:function(t,e,i,s,n,a,o){if(s._transform)return n;var h,l,_,u,p,f,c,m=s._transform=be(t,r,!0,o.parseTransform),d=t.style,g=1e-6,v=ve.length,y=o,T={};if("string"==typeof y.transform&&ye)_=d.cssText,d[ye]=y.transform,d.display="block",h=be(t,null,!1),d.cssText=_;else if("object"==typeof y){if(h={scaleX:se(null!=y.scaleX?y.scaleX:y.scale,m.scaleX),scaleY:se(null!=y.scaleY?y.scaleY:y.scale,m.scaleY),scaleZ:se(null!=y.scaleZ?y.scaleZ:y.scale,m.scaleZ),x:se(y.x,m.x),y:se(y.y,m.y),z:se(y.z,m.z),perspective:se(y.transformPerspective,m.perspective)},c=y.directionalRotation,null!=c)if("object"==typeof c)for(_ in c)y[_]=c[_];else y.rotation=c;h.rotation=re("rotation"in y?y.rotation:"shortRotation"in y?y.shortRotation+"_short":"rotationZ"in y?y.rotationZ:m.rotation,m.rotation,"rotation",T),xe&&(h.rotationX=re("rotationX"in y?y.rotationX:"shortRotationX"in y?y.shortRotationX+"_short":m.rotationX||0,m.rotationX,"rotationX",T),h.rotationY=re("rotationY"in y?y.rotationY:"shortRotationY"in y?y.shortRotationY+"_short":m.rotationY||0,m.rotationY,"rotationY",T)),h.skewX=null==y.skewX?m.skewX:re(y.skewX,m.skewX),h.skewY=null==y.skewY?m.skewY:re(y.skewY,m.skewY),(l=h.skewY-m.skewY)&&(h.skewX+=l,h.rotation+=l)}for(null!=y.force3D&&(m.force3D=y.force3D,f=!0),p=m.force3D||m.z||m.rotationX||m.rotationY||h.z||h.rotationX||h.rotationY||h.perspective,p||null==y.scale||(h.scaleZ=1);--v>-1;)i=ve[v],u=h[i]-m[i],(u>g||-g>u||null!=E[i])&&(f=!0,n=new pe(m,i,m[i],u,n),i in T&&(n.e=T[i]),n.xs0=0,n.plugin=a,s._overwriteProps.push(n.n));return u=y.transformOrigin,(u||xe&&p&&m.zOrigin)&&(ye?(f=!0,i=we,u=(u||G(t,i,r,!1,"50% 50%"))+"",n=new pe(d,i,0,0,n,-1,"transformOrigin"),n.b=d[i],n.plugin=a,xe?(_=m.zOrigin,u=u.split(" "),m.zOrigin=(u.length>2&&(0===_||"0px"!==u[2])?parseFloat(u[2]):_)||0,n.xs0=n.e=d[i]=u[0]+" "+(u[1]||"50%")+" 0px",n=new pe(m,"zOrigin",0,0,n,-1,n.n),n.b=_,n.xs0=n.e=m.zOrigin):n.xs0=n.e=d[i]=u):ee(u+"",m)),f&&(s._transformType=p||3===this._transformType?3:2),n},prefix:!0}),de("boxShadow",{defaultValue:"0px 0px 0px 0px #999",prefix:!0,color:!0,multi:!0,keyword:"inset"}),de("borderRadius",{defaultValue:"0px",parser:function(t,e,i,n,a){e=this.format(e);var o,h,l,_,u,p,f,c,m,d,g,v,y,T,w,x,b=["borderTopLeftRadius","borderTopRightRadius","borderBottomRightRadius","borderBottomLeftRadius"],P=t.style;for(m=parseFloat(t.offsetWidth),d=parseFloat(t.offsetHeight),o=e.split(" "),h=0;b.length>h;h++)this.p.indexOf("border")&&(b[h]=V(b[h])),u=_=G(t,b[h],r,!1,"0px"),-1!==u.indexOf(" ")&&(_=u.split(" "),u=_[0],_=_[1]),p=l=o[h],f=parseFloat(u),v=u.substr((f+"").length),y="="===p.charAt(1),y?(c=parseInt(p.charAt(0)+"1",10),p=
vendor: 4,198 bytes, line 52509
52509p.substr(2),c*=parseFloat(p),g=p.substr((c+"").length-(0>c?1:0))||""):(c=parseFloat(p),g=p.substr((c+"").length)),""===g&&(g=s[i]||v),g!==v&&(T=$(t,"borderLeft",f,v),w=$(t,"borderTop",f,v),"%"===g?(u=100*(T/m)+"%",_=100*(w/d)+"%"):"em"===g?(x=$(t,"borderLeft",1,"em"),u=T/x+"em",_=w/x+"em"):(u=T+"px",_=w+"px"),y&&(p=parseFloat(u)+c+g,l=parseFloat(_)+c+g)),a=fe(P,b[h],u+" "+_,p+" "+l,!1,"0px",a);return a},prefix:!0,formatter:le("0px 0px 0px 0px",!1,!0)}),de("backgroundPosition",{defaultValue:"0 0",parser:function(t,e,i,s,n,a){var o,h,l,_,u,p,f="background-position",m=r||Z(t,null),d=this.format((m?c?m.getPropertyValue(f+"-x")+" "+m.getPropertyValue(f+"-y"):m.getPropertyValue(f):t.currentStyle.backgroundPositionX+" "+t.currentStyle.backgroundPositionY)||"0 0"),g=this.format(e);if(-1!==d.indexOf("%")!=(-1!==g.indexOf("%"))&&(p=G(t,"backgroundImage").replace(k,""),p&&"none"!==p)){for(o=d.split(" "),h=g.split(" "),L.setAttribute("src",p),l=2;--l>-1;)d=o[l],_=-1!==d.indexOf("%"),_!==(-1!==h[l].indexOf("%"))&&(u=0===l?t.offsetWidth-L.width:t.offsetHeight-L.height,o[l]=_?parseFloat(d)/100*u+"px":100*(parseFloat(d)/u)+"%");d=o.join(" ")}return this.parseComplex(t.style,d,g,n,a)},formatter:ee}),de("backgroundSize",{defaultValue:"0 0",formatter:ee}),de("perspective",{defaultValue:"0px",prefix:!0}),de("perspectiveOrigin",{defaultValue:"50% 50%",prefix:!0}),de("transformStyle",{prefix:!0}),de("backfaceVisibility",{prefix:!0}),de("userSelect",{prefix:!0}),de("margin",{parser:_e("marginTop,marginRight,marginBottom,marginLeft")}),de("padding",{parser:_e("paddingTop,paddingRight,paddingBottom,paddingLeft")}),de("clip",{defaultValue:"rect(0px,0px,0px,0px)",parser:function(t,e,i,s,n,a){var o,h,l;return 9>c?(h=t.currentStyle,l=8>c?" ":",",o="rect("+h.clipTop+l+h.clipRight+l+h.clipBottom+l+h.clipLeft+")",e=this.format(e).split(",").join(l)):(o=this.format(G(t,this.p,r,!1,this.dflt)),e=this.format(e)),this.parseComplex(t.style,o,e,n,a)}}),de("textShadow",{defaultValue:"0px 0px 0px #999",color:!0,multi:!0}),de("autoRound,strictUnits",{parser:function(t,e,i,s,r){return r}}),de("border",{defaultValue:"0px solid #000",parser:function(t,e,i,s,n,a){return this.parseComplex(t.style,this.format(G(t,"borderTopWidth",r,!1,"0px")+" "+G(t,"borderTopStyle",r,!1,"solid")+" "+G(t,"borderTopColor",r,!1,"#000")),this.format(e),n,a)},color:!0,formatter:function(t){var e=t.split(" ");return e[0]+" "+(e[1]||"solid")+" "+(t.match(he)||["#000"])[0]}}),de("float,cssFloat,styleFloat",{parser:function(t,e,i,s,r){var n=t.style,a="cssFloat"in n?"cssFloat":"styleFloat";return new pe(n,a,0,0,r,-1,i,!1,0,n[a],e)}});var Re=function(t){var e,i=this.t,s=i.filter||G(this.data,"filter"),r=0|this.s+this.c*t;100===r&&(-1===s.indexOf("atrix(")&&-1===s.indexOf("radient(")&&-1===s.indexOf("oader(")?(i.removeAttribute("filter"),e=!G(this.data,"filter")):(i.filter=s.replace(x,""),e=!0)),e||(this.xn1&&(i.filter=s=s||"alpha(opacity="+r+")"),-1===s.indexOf("opacity")?0===r&&this.xn1||(i.filter=s+" alpha(opacity="+r+")"):i.filter=s.replace(T,"opacity="+r))};de("opacity,alpha,autoAlpha",{defaultValue:"1",parser:function(t,e,i,s,n,a){var o=parseFloat(G(t,"opacity",r,!1,"1")),h=t.style,l="autoAlpha"===i;return"string"==typeof e&&"="===e.charAt(1)&&(e=("-"===e.charAt(0)?-1:1)*parseFloat(e.substr(2))+o),l&&1===o&&"hidden"===G(t,"visibility",r)&&0!==e&&(o=0),U?n=new pe(h,"opacity",o,e-o,n):(n=new pe(h,"opacity",100*o,100*(e-o),n),n.xn1=l?1:0,h.zoom=1,n.type=2,n.b="alpha(opacity="+n.s+")",n.e="alpha(opacity="+(n.s+n.c)+")",n.data=t,n.plugin=a,n.setRatio=Re),l&&(n=new pe(h,"visibility",0,0,n,-1,null,!1,0,0!==o?"inherit":"hidden",0===e?"hidden":"inherit"),n.xs0="inherit",s._overwriteProps.push(n.n),s._overwriteProps.push(i)),n}});var Ae=function(t,e){e&&(t.removeProperty?t.removeProperty(e.replace(P,"-$1").toLowerCase()):t.removeAttribute(e))},Ce=function(t){if(this.t._gsClassPT=this,1===t||0===t){this.t.className=0===t?this.b:this.e;for(var e=this.data,i=this.t.style;e;)e.v?i[e.p]=e.v:Ae(i,e.p),e=e._next;1===t&&this.t._gsClassPT===this&&(this.t._gsClassPT=null)}else this.t.className!==this.e&&(this.t.className=this.e)};de("className",{parser:function(t,e,s,n,a,o,h){var l,_,u,p,f,c=t.className,m=t.
52509style.cssText;if(a=n._classNamePT=new pe(t,s,0,0,a,2),a.setRatio=Ce,a.pr=-11,i=!0,a.b=c,_=W(t,r),u=t._gsClassPT){for(p={},f=u.data;f;)p[f.p]=1,f=f._next;u.setRatio(1)}return t._gsClassPT=a,a.e="="!==e.charAt(1)?e:c.replace(RegExp("\\s*\\b"+e.substr(2)+"\\b"),"")+("+"===e.charAt(0)?" "+e.substr(2):""),n._tween._duration&&(t.className=a.e,l=H(t,_,W(t),h,p),t.className=c,a.data=l.firstMPT,t.style.cssText=m,a=a.xfirst=n.parse(t,l.difs,a,o)),a}});var Oe=function(t){if((1===t||0===t)&&this.data._totalTime===this.data._totalDuration&&"isFromStart"!==this.data.data){var e,i,s,r,n=this.t.style,a=o.transform.parse;if("all"===this.e)n.cssText="",r=!0;else for(e=this.e.split(","),s=e.length;--s>-1;)i=e[s],o[i]&&(o[i].parse===a?r=!0:i="transformOrigin"===i?we:o[i].p),Ae(n,i);r&&(Ae(n,ye),this.t._gsTransform&&delete this.t._gsTransform)}};for(de("clearProps",{parser:function(t,e,s,r,n){return n=new pe(t,s,0,0,n,2),n.setRatio=Oe,n.e=e,n.pr=-10,n.data=r._tween,i=!0,n}}),h="bezier,throwProps,physicsProps,physics2D".split(","),ce=h.length;ce--;)ge(h[ce]);h=a.prototype,h._firstPT=null,h._onInitTween=function(t,e,o){if(!t.nodeType)return!1;this._target=t,this._tween=o,this._vars=e,l=e.autoRound,i=!1,s=e.suffixMap||a.suffixMap,r=Z(t,""),n=this._overwriteProps;var h,p,c,m,d,g,v,y,T,x=t.style;if(_&&""===x.zIndex&&(h=G(t,"zIndex",r),("auto"===h||""===h)&&(x.zIndex=0)),"string"==typeof e&&(m=x.cssText,h=W(t,r),x.cssText=m+";"+e,h=H(t,h,W(t)).difs,!U&&w.test(e)&&(h.opacity=parseFloat(RegExp.$1)),e=h,x.cssText=m),this._firstPT=p=this.parse(t,e,null),this._transformType){for(T=3===this._transformType,ye?u&&(_=!0,""===x.zIndex&&(v=G(t,"zIndex",r),("auto"===v||""===v)&&(x.zIndex=0)),f&&(x.WebkitBackfaceVisibility=this._vars.WebkitBackfaceVisibility||(T?"visible":"hidden"))):x.zoom=1,c=p;c&&c._next;)c=c._next;y=new pe(t,"transform",0,0,null,2),this._linkCSSP(y,null,c),y.setRatio=T&&xe?Se:ye?ke:Pe,y.data=this._transform||be(t,r,!0),n.pop()}if(i){for(;p;){for(g=p._next,c=m;c&&c.pr>p.pr;)c=c._next;(p._prev=c?c._prev:d)?p._prev._next=p:m=p,(p._next=c)?c._prev=p:d=p,p=g}this._firstPT=m}return!0},h.parse=function(t,e,i,n){var a,h,_,u,p,f,c,m,d,g,v=t.style;for(a in e)f=e[a],h=o[a],h?i=h.parse(t,f,a,this,i,n,e):(p=G(t,a,r)+"",d="string"==typeof f,"color"===a||"fill"===a||"stroke"===a||-1!==a.indexOf("Color")||d&&b.test(f)?(d||(f=oe(f),f=(f.length>3?"rgba(":"rgb(")+f.join(",")+")"),i=fe(v,a,p,f,!0,"transparent",i,0,n)):!d||-1===f.indexOf(" ")&&-1===f.indexOf(",")?(_=parseFloat(p),c=_||0===_?p.substr((_+"").length):"",(""===p||"auto"===p)&&("width"===a||"height"===a?(_=te(t,a,r),c="px"):"left"===a||"top"===a?(_=Q(t,a,r),c="px"):(_="opacity"!==a?0:1,c="")),g=d&&"="===f.charAt(1),g?(u=parseInt(f.charAt(0)+"1",10),f=f.substr(2),u*=parseFloat(f),m=f.replace(y,"")):(u=parseFloat(f),m=d?f.substr((u+"").length)||"":""),""===m&&(m=s[a]||c),f=u||0===u?(g?u+_:u)+m:e[a],c!==m&&""!==m&&(u||0===u)&&(_||0===_)&&(_=$(t,a,_,c),"%"===m?(_/=$(t,a,100,"%")/100,_>100&&(_=100),e.strictUnits!==!0&&(p=_+"%")):"em"===m?_/=$(t,a,1,"em"):(u=$(t,a,u,m),m="px"),g&&(u||0===u)&&(f=u+_+m)),g&&(u+=_),!_&&0!==_||!u&&0!==u?void 0!==v[a]&&(f||"NaN"!=f+""&&null!=f)?(i=new pe(v,a,u||_||0,0,i,-1,a,!1,0,p,f),i.xs0="none"!==f||"display"!==a&&-1===a.indexOf("Style")?f:p):j("invalid "+a+" tween value: "+e[a]):(i=new pe(v,a,_,u-_,i,0,a,l!==!1&&("px"===m||"zIndex"===a),0,p,f),i.xs0=m)):i=fe(v,a,p,f,!0,null,i,0,n)),n&&i&&!i.plugin&&(i.plugin=n);return i},h.setRatio=function(t){var e,i,s,r=this._firstPT,n=1e-6;if(1!==t||this._tween._time!==this._tween._duration&&0!==this._tween._time)if(t||this._tween._time!==this._tween._duration&&0!==this._tween._time||this._tween._rawPrevTime===-1e-6)for(;r;){if(e=r.c*t+r.s,r.r?e=e>0?0|e+.5:0|e-.5:n>e&&e>-n&&(e=0),r.type)if(1===r.type)if(s=r.l,2===s)r.t[r.p]=r.xs0+e+r.xs1+r.xn1+r.xs2;else if(3===s)r.t[r.p]=r.xs0+e+r.xs1+r.xn1+r.xs2+r.xn2+r.xs3;else if(4===s)r.t[r.p]=r.xs0+e+r.xs1+r.xn1+r.xs2+r.xn2+r.xs3+r.xn3+r.xs4;else if(5===s)r.t[r.p]=r.xs0+e+r.xs1+r.xn1+r.xs2+r.xn2+r.xs3+r.xn3+r.xs4+r.xn4+r.xs5;else{for(i=r.xs0+e+r.xs1,s=1;r.l>s;s++)i+=r["xn"+s]+r["xs"+(s+1)];r.t[r.p]=i}else-1===r.type?r.t[r.p]=r.xs0:r.setRatio&&r.setRatio(t);else r.t[r.p]=e+r.xs0;r=r._next}else for(;r;)2!==r.type?r.t[r.p]=r.b:r.setRatio(t),r=r._next;else for(;r;)2!==r.type?r.t[r.p]=r.e:r.setRatio(t),r=r._next},h._enableTransforms=function(t){this._transformType=t||3===this._transformType?3:2,this._transform=this._transform||be(this._target,r,!0)}
52509,h._linkCSSP=function(t,e,i,s){return t&&(e&&(e._prev=t),t._next&&(t._next._prev=t._prev),t._prev?t._prev._next=t._next:this._firstPT===t&&(this._firstPT=t._next,s=!0),i?i._next=t:s||null!==this._firstPT||(this._firstPT=t),t._next=e,t._prev=i),t},h._kill=function(e){var i,s,r,n=e;if(e.autoAlpha||e.alpha){n={};for(s in e)n[s]=e[s];n.opacity=1,n.autoAlpha&&(n.visibility=1)}return e.className&&(i=this._classNamePT)&&(r=i.xfirst,r&&r._prev?this._linkCSSP(r._prev,i._next,r._prev._prev):r===this._firstPT&&(this._firstPT=i._next),i._next&&this._linkCSSP(i._next,i._next._next,r._prev),this._classNamePT=null),t.prototype._kill.call(this,n)};var De=function(t,e,i){var s,r,n,a;if(t.slice)for(r=t.length;--r>-1;)De(t[r],e,i);else for(s=t.childNodes,r=s.length;--r>-1;)n=s[r],a=n.type,n.style&&(e.push(W(n)),i&&i.push(n)),1!==a&&9!==a&&11!==a||!n.childNodes.length||De(n,e,i)};return a.cascadeTo=function(t,i,s){var r,n,a,o=e.to(t,i,s),h=[o],l=[],_=[],u=[],p=e._internals.reservedProps;for(t=o._targets||o.target,De(t,l,u),o.render(i,!0),De(t,_),o.render(0,!0),o._enabled(!0),r=u.length;--r>-1;)if(n=H(u[r],l[r],_[r]),n.firstMPT){n=n.difs;for(a in s)p[a]&&(n[a]=s[a]);h.push(e.to(u[r],i,n))}return h},t.activate([a]),a},!0),function(){var t=window._gsDefine.plugin({propName:"roundProps",priority:-1,API:2,init:function(t,e,i){return this._tween=i,!0}}),e=t.prototype;e._onInitAllProps=function(){for(var t,e,i,s=this._tween,r=s.vars.roundProps instanceof Array?s.vars.roundProps:s.vars.roundProps.split(","),n=r.length,a={},o=s._propLookup.roundProps;--n>-1;)a[r[n]]=1;for(n=r.length;--n>-1;)for(t=r[n],e=s._firstPT;e;)i=e._next,e.pg?e.t._roundProps(a,!0):e.n===t&&(this._add(e.t,t,e.s,e.c),i&&(i._prev=e._prev),e._prev?e._prev._next=i:s._firstPT===e&&(s._firstPT=i),e._next=e._prev=null,s._propLookup[t]=o),e=i;return!1},e._add=function(t,e,i,s){this._addTween(t,e,i,i+s,e,!0),this._overwriteProps.push(e)}}(),window._gsDefine.plugin({propName:"attr",API:2,init:function(t,e){var i;if("function"!=typeof t.setAttribute)return!1;this._target=t,this._proxy={}; 52510for(i in e)this._addTween(this._proxy,i,parseFloat(t.getAttribute(i)),e[i],i)&&this._overwriteProps.push(i);return!0},set:function(t){this._super.setRatio.call(this,t);for(var e,i=this._overwriteProps,s=i.length;--s>-1;)e=i[s],this._target.setAttribute(e,this._proxy[e]+"")}}),window._gsDefine.plugin({propName:"directionalRotation",API:2,init:function(t,e){"object"!=typeof e&&(e={rotation:e}),this.finals={};var i,s,r,n,a,o,h=e.useRadians===!0?2*Math.PI:360,l=1e-6;for(i in e)"useRadians"!==i&&(o=(e[i]+"").split("_"),s=o[0],r=parseFloat("function"!=typeof t[i]?t[i]:t[i.indexOf("set")||"function"!=typeof t["get"+i.substr(3)]?i:"get"+i.substr(3)]()),n=this.finals[i]="string"==typeof s&&"="===s.charAt(1)?r+parseInt(s.charAt(0)+"1",10)*Number(s.substr(2)):Number(s)||0,a=n-r,o.length&&(s=o.join("_"),-1!==s.indexOf("short")&&(a%=h,a!==a%(h/2)&&(a=0>a?a+h:a-h)),-1!==s.indexOf("_cw")&&0>a?a=(a+9999999999*h)%h-(0|a/h)*h:-1!==s.indexOf("ccw")&&a>0&&(a=(a-9999999999*h)%h-(0|a/h)*h)),(a>l||-l>a)&&(this._addTween(t,i,r,r+a,i),this._overwriteProps.push(i)));return!0},set:function(t){var e;if(1!==t)this._super.setRatio.call(this,t);else for(e=this._firstPT;e;)e.f?e.t[e.p](this.finals[e.p]):e.t[e.p]=this.finals[e.p],e=e._next}})._autoCSS=!0,window._gsDefine("easing.Back",["easing.Ease"],function(t){var e,i,s,r=window.GreenSockGlobals||window,n=r.com.greensock,a=2*Math.PI,o=Math.PI/2,h=n._class,l=function(e,i){var s=h("easing."+e,function(){},!0),r=s.prototype=new t;return r.constructor=s,r.getRatio=i,s},_=t.register||function(){},u=function(t,e,i,s){var r=h("easing."+t,{easeOut:new e,easeIn:new i,easeInOut:new s},!0);return _(r,t),r},p=function(t,e,i){this.t=t,this.v=e,i&&(this.next=i,i.prev=this,this.c=i.v-e,this.gap=i.t-t)},f=function(e,i){var s=h("easing."+e,function(t){this._p1=t||0===t?t:1.70158,this._p2=1.525*this._p1},!0),r=s.prototype=new t;return r.constructor=s,r.getRatio=i,r.config=function(t){return new s(t)},s},c=u("Back",f("BackOut",function(t){return(t-=1)*t*((this._p1+1)*t+this._p1)+1}),f("BackIn",function(t){return t*t*((this._p1+1)*t-this._p1)}),f("BackInOut",function(t){return 1>(t*=2)?.5*t*t*((this._p2+1)*t-this._p2):.5*((t-=2)*t*((this._p2+1)*t+this._p2)+2)}
vendor: 4,316 bytes, line 52510
52510)),m=h("easing.SlowMo",function(t,e,i){e=e||0===e?e:.7,null==t?t=.7:t>1&&(t=1),this._p=1!==t?e:0,this._p1=(1-t)/2,this._p2=t,this._p3=this._p1+this._p2,this._calcEnd=i===!0},!0),d=m.prototype=new t;return d.constructor=m,d.getRatio=function(t){var e=t+(.5-t)*this._p;return this._p1>t?this._calcEnd?1-(t=1-t/this._p1)*t:e-(t=1-t/this._p1)*t*t*t*e:t>this._p3?this._calcEnd?1-(t=(t-this._p3)/this._p1)*t:e+(t-e)*(t=(t-this._p3)/this._p1)*t*t*t:this._calcEnd?1:e},m.ease=new m(.7,.7),d.config=m.config=function(t,e,i){return new m(t,e,i)},e=h("easing.SteppedEase",function(t){t=t||1,this._p1=1/t,this._p2=t+1},!0),d=e.prototype=new t,d.constructor=e,d.getRatio=function(t){return 0>t?t=0:t>=1&&(t=.999999999),(this._p2*t>>0)*this._p1},d.config=e.config=function(t){return new e(t)},i=h("easing.RoughEase",function(e){e=e||{};for(var i,s,r,n,a,o,h=e.taper||"none",l=[],_=0,u=0|(e.points||20),f=u,c=e.randomize!==!1,m=e.clamp===!0,d=e.template instanceof t?e.template:null,g="number"==typeof e.strength?.4*e.strength:.4;--f>-1;)i=c?Math.random():1/u*f,s=d?d.getRatio(i):i,"none"===h?r=g:"out"===h?(n=1-i,r=n*n*g):"in"===h?r=i*i*g:.5>i?(n=2*i,r=.5*n*n*g):(n=2*(1-i),r=.5*n*n*g),c?s+=Math.random()*r-.5*r:f%2?s+=.5*r:s-=.5*r,m&&(s>1?s=1:0>s&&(s=0)),l[_++]={x:i,y:s};for(l.sort(function(t,e){return t.x-e.x}),o=new p(1,1,null),f=u;--f>-1;)a=l[f],o=new p(a.x,a.y,o);this._prev=new p(0,0,0!==o.t?o:o.next)},!0),d=i.prototype=new t,d.constructor=i,d.getRatio=function(t){var e=this._prev;if(t>e.t){for(;e.next&&t>=e.t;)e=e.next;e=e.prev}else for(;e.prev&&e.t>=t;)e=e.prev;return this._prev=e,e.v+(t-e.t)/e.gap*e.c},d.config=function(t){return new i(t)},i.ease=new i,u("Bounce",l("BounceOut",function(t){return 1/2.75>t?7.5625*t*t:2/2.75>t?7.5625*(t-=1.5/2.75)*t+.75:2.5/2.75>t?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375}),l("BounceIn",function(t){return 1/2.75>(t=1-t)?1-7.5625*t*t:2/2.75>t?1-(7.5625*(t-=1.5/2.75)*t+.75):2.5/2.75>t?1-(7.5625*(t-=2.25/2.75)*t+.9375):1-(7.5625*(t-=2.625/2.75)*t+.984375)}),l("BounceInOut",function(t){var e=.5>t;return t=e?1-2*t:2*t-1,t=1/2.75>t?7.5625*t*t:2/2.75>t?7.5625*(t-=1.5/2.75)*t+.75:2.5/2.75>t?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375,e?.5*(1-t):.5*t+.5})),u("Circ",l("CircOut",function(t){return Math.sqrt(1-(t-=1)*t)}),l("CircIn",function(t){return-(Math.sqrt(1-t*t)-1)}),l("CircInOut",function(t){return 1>(t*=2)?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)})),s=function(e,i,s){var r=h("easing."+e,function(t,e){this._p1=t||1,this._p2=e||s,this._p3=this._p2/a*(Math.asin(1/this._p1)||0)},!0),n=r.prototype=new t;return n.constructor=r,n.getRatio=i,n.config=function(t,e){return new r(t,e)},r},u("Elastic",s("ElasticOut",function(t){return this._p1*Math.pow(2,-10*t)*Math.sin((t-this._p3)*a/this._p2)+1},.3),s("ElasticIn",function(t){return-(this._p1*Math.pow(2,10*(t-=1))*Math.sin((t-this._p3)*a/this._p2))},.3),s("ElasticInOut",function(t){return 1>(t*=2)?-.5*this._p1*Math.pow(2,10*(t-=1))*Math.sin((t-this._p3)*a/this._p2):.5*this._p1*Math.pow(2,-10*(t-=1))*Math.sin((t-this._p3)*a/this._p2)+1},.45)),u("Expo",l("ExpoOut",function(t){return 1-Math.pow(2,-10*t)}),l("ExpoIn",function(t){return Math.pow(2,10*(t-1))-.001}),l("ExpoInOut",function(t){return 1>(t*=2)?.5*Math.pow(2,10*(t-1)):.5*(2-Math.pow(2,-10*(t-1)))})),u("Sine",l("SineOut",function(t){return Math.sin(t*o)}),l("SineIn",function(t){return-Math.cos(t*o)+1}),l("SineInOut",function(t){return-.5*(Math.cos(Math.PI*t)-1)})),h("easing.EaseLookup",{find:function(e){return t.map[e]}},!0),_(r.SlowMo,"SlowMo","ease,"),_(i,"RoughEase","ease,"),_(e,"SteppedEase","ease,"),c},!0)}),function(t){"use strict";var e=t.GreenSockGlobals||t;if(!e.TweenLite){var i,s,r,n,a,o=function(t){var i,s=t.split("."),r=e;for(i=0;s.length>i;i++)r[s[i]]=r=r[s[i]]||{};return r},h=o("com.greensock"),l=1e-10,_=[].slice,u=function(){},p=function(){var t=Object.prototype.toString,e=t.call([]);return function(i){return i instanceof Array||"object"==typeof i&&!!i.push&&t.call(i)===e}}(),f={},c=function(i,s,r,n){this.sc=f[i]?f[i].sc:[],f[i]=this,this.gsClass=null,this.func=r;var a=[];this.check=function(h){for(var l,_,u,p,m=s.length,d=m;--m>-1;)(l=f[s[m]]||new c(s[m],[])).gsClass?(a[m]=l.gsClass,d--):h&&l.sc.push(this);if(0===d&&r)for(_=("com.greensock."+i).split("."),u=_.pop(),p=o(_.j
52510oin("."))[u]=this.gsClass=r.apply(r,a),n&&(e[u]=p,"function"==typeof define&&define.amd?define((t.GreenSockAMDPath?t.GreenSockAMDPath+"/":"")+i.split(".").join("/"),[],function(){return p}):"undefined"!=typeof module&&module.exports&&(module.exports=p)),m=0;this.sc.length>m;m++)this.sc[m].check()},this.check(!0)},m=t._gsDefine=function(t,e,i,s){return new c(t,e,i,s)},d=h._class=function(t,e,i){return e=e||function(){},m(t,[],function(){return e},i),e};m.globals=e;var g=[0,0,1,1],v=[],y=d("easing.Ease",function(t,e,i,s){this._func=t,this._type=i||0,this._power=s||0,this._params=e?g.concat(e):g},!0),T=y.map={},w=y.register=function(t,e,i,s){for(var r,n,a,o,l=e.split(","),_=l.length,u=(i||"easeIn,easeOut,easeInOut").split(",");--_>-1;)for(n=l[_],r=s?d("easing."+n,null,!0):h.easing[n]||{},a=u.length;--a>-1;)o=u[a],T[n+"."+o]=T[o+n]=r[o]=t.getRatio?t:t[o]||new t};for(r=y.prototype,r._calcEnd=!1,r.getRatio=function(t){if(this._func)return this._params[0]=t,this._func.apply(null,this._params);var e=this._type,i=this._power,s=1===e?1-t:2===e?t:.5>t?2*t:2*(1-t);return 1===i?s*=s:2===i?s*=s*s:3===i?s*=s*s*s:4===i&&(s*=s*s*s*s),1===e?1-s:2===e?s:.5>t?s/2:1-s/2},i=["Linear","Quad","Cubic","Quart","Quint,Strong"],s=i.length;--s>-1;)r=i[s]+",Power"+s,w(new y(null,null,1,s),r,"easeOut",!0),w(new y(null,null,2,s),r,"easeIn"+(0===s?",easeNone":"")),w(new y(null,null,3,s),r,"easeInOut");T.linear=h.easing.Linear.easeIn,T.swing=h.easing.Quad.easeInOut;var x=d("events.EventDispatcher",function(t){this._listeners={},this._eventTarget=t||this});r=x.prototype,r.addEventListener=function(t,e,i,s,r){r=r||0;var o,h,l=this._listeners[t],_=0;for(null==l&&(this._listeners[t]=l=[]),h=l.length;--h>-1;)o=l[h],o.c===e&&o.s===i?l.splice(h,1):0===_&&r>o.pr&&(_=h+1);l.splice(_,0,{c:e,s:i,up:s,pr:r}),this!==n||a||n.wake()},r.removeEventListener=function(t,e){var i,s=this._listeners[t];if(s)for(i=s.length;--i>-1;)if(s[i].c===e)return s.splice(i,1),void 0},r.dispatchEvent=function(t){var e,i,s,r=this._listeners[t];if(r)for(e=r.length,i=this._eventTarget;--e>-1;)s=r[e],s.up?s.c.call(s.s||i,{type:t,target:i}):s.c.call(s.s||i)};var b=t.requestAnimationFrame,P=t.cancelAnimationFrame,S=Date.now||function(){return(new Date).getTime()},k=S();for(i=["ms","moz","webkit","o"],s=i.length;--s>-1&&!b;)b=t[i[s]+"RequestAnimationFrame"],P=t[i[s]+"CancelAnimationFrame"]||t[i[s]+"CancelRequestAnimationFrame"];d("Ticker",function(t,e){var i,s,r,o,h,l=this,_=S(),p=e!==!1&&b,f=function(t){k=S(),l.time=(k-_)/1e3;var e,n=l.time-h;(!i||n>0||t===!0)&&(l.frame++,h+=n+(n>=o?.004:o-n),e=!0),t!==!0&&(r=s(f)),e&&l.dispatchEvent("tick")};x.call(l),l.time=l.frame=0,l.tick=function(){f(!0)},l.sleep=function(){null!=r&&(p&&P?P(r):clearTimeout(r),s=u,r=null,l===n&&(a=!1))},l.wake=function(){null!==r&&l.sleep(),s=0===i?u:p&&b?b:function(t){return setTimeout(t,0|1e3*(h-l.time)+1)},l===n&&(a=!0),f(2)},l.fps=function(t){return arguments.length?(i=t,o=1/(i||60),h=this.time+o,l.wake(),void 0):i},l.useRAF=function(t){return arguments.length?(l.sleep(),p=t,l.fps(i),void 0):p},l.fps(t),setTimeout(function(){p&&(!r||5>l.frame)&&l.useRAF(!1)},1500)}),r=h.Ticker.prototype=new h.events.EventDispatcher,r.constructor=h.Ticker;var R=d("core.Animation",function(t,e){if(this.vars=e=e||{},this._duration=this._totalDuration=t||0,this._delay=Number(e.delay)||0,this._timeScale=1,this._active=e.immediateRender===!0,this.data=e.data,this._reversed=e.reversed===!0,U){a||n.wake();var i=this.vars.useFrames?z:U;i.add(this,i._time),this.vars.paused&&this.paused(!0)}});n=R.ticker=new h.Ticker,r=R.prototype,r._dirty=r._gc=r._initted=r._paused=!1,r._totalTime=r._time=0,r._rawPrevTime=-1,r._next=r._last=r._onUpdate=r._timeline=r.timeline=null,r._paused=!1;var A=function(){a&&S()-k>2e3&&n.wake(),setTimeout(A,2e3)};A(),r.play=function(t,e){return arguments.length&&this.seek(t,e),this.reversed(!1).paused(!1)},r.pause=function(t,e){return arguments.length&&this.seek(t,e),this.paused(!0)},r.resume=function(t,e){return arguments.length&&this.seek(t,e),this.paused(!1)},r.seek=function(t,e){return this.totalTime(Number(t),e!==!1)},r.restart=function(t,e){return this.reversed(!1).paused(!1).totalTime(t?-this._delay:0,e!==!1,!0)},r.reverse=function(t,e){return arguments.length&&this.seek(t||this.totalDuration(),e),this.reversed(!0).paused(!1)},r.render=function(){},r.invalidate=function(){return this},r.isActive=function(){var t,e=this._timeline,i=this._startTime;return!e||!this._gc&&!this._paused&&e.isActive()&&(t=e.rawTime())>=i&&i+this.totalDuration()/this._timeScale>t},r._enabled=function(t,e){return a||n.wake(),this._gc=!t,this._active=this.isActive(),e!==!0&&(t&&!this.timeline?this._timeline.add(this,this._startTime-this._delay):!t&&this.timeline&&this._timeline._remove(this,!0)),!1},r._kill=function(){return this._enabled(!1,!1)},r.kill=function(t,e){return this._kill(t,e),this},r._uncache=function(t){for(var e=t?this:this.timeline;e;)e._dirty=!0,e=e.timeline;return this},r._swapSelfInParams=function(t){for(var e=t.length,i=t.concat();--e>-1;)"{self}"===t[e]&&(i[e]=this);return i}
vendor: 4,483 bytes, line 52510
52510,r.eventCallback=function(t,e,i,s){if("on"===(t||"").substr(0,2)){var r=this.vars;if(1===arguments.length)return r[t];null==e?delete r[t]:(r[t]=e,r[t+"Params"]=p(i)&&-1!==i.join("").indexOf("{self}")?this._swapSelfInParams(i):i,r[t+"Scope"]=s),"onUpdate"===t&&(this._onUpdate=e)}return this},r.delay=function(t){return arguments.length?(this._timeline.smoothChildTiming&&this.startTime(this._startTime+t-this._delay),this._delay=t,this):this._delay},r.duration=function(t){return arguments.length?(this._duration=this._totalDuration=t,this._uncache(!0),this._timeline.smoothChildTiming&&this._time>0&&this._time<this._duration&&0!==t&&this.totalTime(this._totalTime*(t/this._duration),!0),this):(this._dirty=!1,this._duration)},r.totalDuration=function(t){return this._dirty=!1,arguments.length?this.duration(t):this._totalDuration},r.time=function(t,e){return arguments.length?(this._dirty&&this.totalDuration(),this.totalTime(t>this._duration?this._duration:t,e)):this._time},r.totalTime=function(t,e,i){if(a||n.wake(),!arguments.length)return this._totalTime;if(this._timeline){if(0>t&&!i&&(t+=this.totalDuration()),this._timeline.smoothChildTiming){this._dirty&&this.totalDuration();var s=this._totalDuration,r=this._timeline;if(t>s&&!i&&(t=s),this._startTime=(this._paused?this._pauseTime:r._time)-(this._reversed?s-t:t)/this._timeScale,r._dirty||this._uncache(!1),r._timeline)for(;r._timeline;)r._timeline._time!==(r._startTime+r._totalTime)/r._timeScale&&r.totalTime(r._totalTime,!0),r=r._timeline}this._gc&&this._enabled(!0,!1),(this._totalTime!==t||0===this._duration)&&this.render(t,e,!1)}return this},r.progress=r.totalProgress=function(t,e){return arguments.length?this.totalTime(this.duration()*t,e):this._time/this.duration()},r.startTime=function(t){return arguments.length?(t!==this._startTime&&(this._startTime=t,this.timeline&&this.timeline._sortChildren&&this.timeline.add(this,t-this._delay)),this):this._startTime},r.timeScale=function(t){if(!arguments.length)return this._timeScale;if(t=t||l,this._timeline&&this._timeline.smoothChildTiming){var e=this._pauseTime,i=e||0===e?e:this._timeline.totalTime();this._startTime=i-(i-this._startTime)*this._timeScale/t}return this._timeScale=t,this._uncache(!1)},r.reversed=function(t){return arguments.length?(t!=this._reversed&&(this._reversed=t,this.totalTime(this._totalTime,!0)),this):this._reversed},r.paused=function(t){if(!arguments.length)return this._paused;if(t!=this._paused&&this._timeline){a||t||n.wake();var e=this._timeline,i=e.rawTime(),s=i-this._pauseTime;!t&&e.smoothChildTiming&&(this._startTime+=s,this._uncache(!1)),this._pauseTime=t?i:null,this._paused=t,this._active=this.isActive(),!t&&0!==s&&this._initted&&this.duration()&&this.render(e.smoothChildTiming?this._totalTime:(i-this._startTime)/this._timeScale,!0,!0)}return this._gc&&!t&&this._enabled(!0,!1),this};var C=d("core.SimpleTimeline",function(t){R.call(this,0,t),this.autoRemoveChildren=this.smoothChildTiming=!0});r=C.prototype=new R,r.constructor=C,r.kill()._gc=!1,r._first=r._last=null,r._sortChildren=!1,r.add=r.insert=function(t,e){var i,s;if(t._startTime=Number(e||0)+t._delay,t._paused&&this!==t._timeline&&(t._pauseTime=t._startTime+(this.rawTime()-t._startTime)/t._timeScale),t.timeline&&t.timeline._remove(t,!0),t.timeline=t._timeline=this,t._gc&&t._enabled(!0,!0),i=this._last,this._sortChildren)for(s=t._startTime;i&&i._startTime>s;)i=i._prev;return i?(t._next=i._next,i._next=t):(t._next=this._first,this._first=t),t._next?t._next._prev=t:this._last=t,t._prev=i,this._timeline&&this._uncache(!0),this},r._remove=function(t,e){return t.timeline===this&&(e||t._enabled(!1,!0),t.timeline=null,t._prev?t._prev._next=t._next:this._first===t&&(this._first=t._next),t._next?t._next._prev=t._prev:this._last===t&&(this._last=t._prev),this._timeline&&this._uncache(!0)),this},r.render=function(t,e,i){var s,r=this._first;for(this._totalTime=this._time=this._rawPrevTime=t;r;)s=r._next,(r._active||t>=r._startTime&&!r._paused)&&(r._reversed?r.render((r._dirty?r.totalDuration():r._totalDuration)-(t-r._startTime)*r._timeScale,e,i):r.render((t-r._startTime)*r._timeScale,e,i)),r=s},r.rawTime=function(){return a||n.wake(),this._totalTime};var O=d("TweenLite",function(e,i,s){if(R.call(this,i,s),this.render=O.prototype.render,null==e)throw"Cannot tween a null target.";this.target=e="string"!=typeof e?e:O.selector(e)||e;var r,n,a,o=e.jquery||e.length&&e!==t&&e[0]&&(e[0]===t||e[0].nodeType&&e[0].style&&!e.nodeType),h=this.vars.over
52510write;if(this._overwrite=h=null==h?X[O.defaultOverwrite]:"number"==typeof h?h>>0:X[h],(o||e instanceof Array||e.push&&p(e))&&"number"!=typeof e[0])for(this._targets=a=_.call(e,0),this._propLookup=[],this._siblings=[],r=0;a.length>r;r++)n=a[r],n?"string"!=typeof n?n.length&&n!==t&&n[0]&&(n[0]===t||n[0].nodeType&&n[0].style&&!n.nodeType)?(a.splice(r--,1),this._targets=a=a.concat(_.call(n,0))):(this._siblings[r]=Y(n,this,!1),1===h&&this._siblings[r].length>1&&j(n,this,null,1,this._siblings[r])):(n=a[r--]=O.selector(n),"string"==typeof n&&a.splice(r+1,1)):a.splice(r--,1);else this._propLookup={},this._siblings=Y(e,this,!1),1===h&&this._siblings.length>1&&j(e,this,null,1,this._siblings);(this.vars.immediateRender||0===i&&0===this._delay&&this.vars.immediateRender!==!1)&&this.render(-this._delay,!1,!0)},!0),D=function(e){return e.length&&e!==t&&e[0]&&(e[0]===t||e[0].nodeType&&e[0].style&&!e.nodeType)},M=function(t,e){var i,s={};for(i in t)L[i]||i in e&&"x"!==i&&"y"!==i&&"width"!==i&&"height"!==i&&"className"!==i&&"border"!==i||!(!E[i]||E[i]&&E[i]._autoCSS)||(s[i]=t[i],delete t[i]);t.css=s};r=O.prototype=new R,r.constructor=O,r.kill()._gc=!1,r.ratio=0,r._firstPT=r._targets=r._overwrittenProps=r._startAt=null,r._notifyPluginsOfEnabled=!1,O.version="1.11.2",O.defaultEase=r._ease=new y(null,null,1,1),O.defaultOverwrite="auto",O.ticker=n,O.autoSleep=!0,O.selector=t.$||t.jQuery||function(e){return t.$?(O.selector=t.$,t.$(e)):t.document?t.document.getElementById("#"===e.charAt(0)?e.substr(1):e):e};var I=O._internals={isArray:p,isSelector:D},E=O._plugins={},F=O._tweenLookup={},N=0,L=I.reservedProps={ease:1,delay:1,overwrite:1,onComplete:1,onCompleteParams:1,onCompleteScope:1,useFrames:1,runBackwards:1,startAt:1,onUpdate:1,onUpdateParams:1,onUpdateScope:1,onStart:1,onStartParams:1,onStartScope:1,onReverseComplete:1,onReverseCompleteParams:1,onReverseCompleteScope:1,onRepeat:1,onRepeatParams:1,onRepeatScope:1,easeParams:1,yoyo:1,immediateRender:1,repeat:1,repeatDelay:1,data:1,paused:1,reversed:1,autoCSS:1},X={none:0,all:1,auto:2,concurrent:3,allOnStart:4,preexisting:5,"true":1,"false":0},z=R._rootFramesTimeline=new C,U=R._rootTimeline=new C;U._startTime=n.time,z._startTime=n.frame,U._active=z._active=!0,R._updateRoot=function(){if(U.render((n.time-U._startTime)*U._timeScale,!1,!1),z.render((n.frame-z._startTime)*z._timeScale,!1,!1),!(n.frame%120)){var t,e,i;for(i in F){for(e=F[i].tweens,t=e.length;--t>-1;)e[t]._gc&&e.splice(t,1);0===e.length&&delete F[i]}if(i=U._first,(!i||i._paused)&&O.autoSleep&&!z._first&&1===n._listeners.tick.length){for(;i&&i._paused;)i=i._next;i||n.sleep()}}},n.addEventListener("tick",R._updateRoot);var Y=function(t,e,i){var s,r,n=t._gsTweenID;if(F[n||(t._gsTweenID=n="t"+N++)]||(F[n]={target:t,tweens:[]}),e&&(s=F[n].tweens,s[r=s.length]=e,i))for(;--r>-1;)s[r]===e&&s.splice(r,1);return F[n].tweens},j=function(t,e,i,s,r){var n,a,o,h;if(1===s||s>=4){for(h=r.length,n=0;h>n;n++)if((o=r[n])!==e)o._gc||o._enabled(!1,!1)&&(a=!0);else if(5===s)break;return a}var _,u=e._startTime+l,p=[],f=0,c=0===e._duration;for(n=r.length;--n>-1;)(o=r[n])===e||o._gc||o._paused||(o._timeline!==e._timeline?(_=_||B(e,0,c),0===B(o,_,c)&&(p[f++]=o)):u>=o._startTime&&o._startTime+o.totalDuration()/o._timeScale+l>u&&((c||!o._initted)&&2e-10>=u-o._startTime||(p[f++]=o)));for(n=f;--n>-1;)o=p[n],2===s&&o._kill(i,t)&&(a=!0),(2!==s||!o._firstPT&&o._initted)&&o._enabled(!1,!1)&&(a=!0);return a},B=function(t,e,i){for(var s=t._timeline,r=s._timeScale,n=t._startTime;s._timeline;){if(n+=s._startTime,r*=s._timeScale,s._paused)return-100;s=s._timeline}return n/=r,n>e?n-e:i&&n===e||!t._initted&&2*l>n-e?l:(n+=t.totalDuration()/t._timeScale/r)>e+l?0:n-e-l};r._init=function(){var t,e,i,s,r=this.vars,n=this._overwrittenProps,a=this._duration,o=r.immediateRender,h=r.ease;if(r.startAt){if(this._startAt&&this._startAt.render(-1,!0),r.startAt.overwrite=0,r.startAt.immediateRender=!0,this._startAt=O.to(this.target,0,r.startAt),o)if(this._time>0)this._startAt=null;else if(0!==a)return}else if(r.runBackwards&&0!==a)if(this._startAt)this._startAt.render(-1,!0),this._startAt=null;else{i={};for(s in r)L[s]&&"autoCSS"!==s||(i[s]=r[s]);if(i.overwrite=0,i.data="isFromStart",this._startAt=O.to(this.target,0,i),r.immediateRender){if(0===this._time)return}else this._startAt.render(-1,!0)}if(this._ease=h?h instanceof y?r.easeParams instanceof Array?h.config.apply(h,r.easeParams):h:"function"==typeof h?new y(
52510h,r.easeParams):T[h]||O.defaultEase:O.defaultEase,this._easeType=this._ease._type,this._easePower=this._ease._power,this._firstPT=null,this._targets)for(t=this._targets.length;--t>-1;)this._initProps(this._targets[t],this._propLookup[t]={},this._siblings[t],n?n[t]:null)&&(e=!0);else e=this._initProps(this.target,this._propLookup,this._siblings,n);if(e&&O._onPluginEvent("_onInitAllProps",this),n&&(this._firstPT||"function"!=typeof this.target&&this._enabled(!1,!1)),r.runBackwards)for(i=this._firstPT;i;)i.s+=i.c,i.c=-i.c,i=i._next;this._onUpdate=r.onUpdate,this._initted=!0},r._initProps=function(e,i,s,r){var n,a,o,h,l,_;if(null==e)return!1;this.vars.css||e.style&&e!==t&&e.nodeType&&E.css&&this.vars.autoCSS!==!1&&M(this.vars,e);for(n in this.vars){if(_=this.vars[n],L[n])_&&(_ instanceof Array||_.push&&p(_))&&-1!==_.join("").indexOf("{self}")&&(this.vars[n]=_=this._swapSelfInParams(_,this));else if(E[n]&&(h=new E[n])._onInitTween(e,this.vars[n],this)){for(this._firstPT=l={_next:this._firstPT,t:h,p:"setRatio",s:0,c:1,f:!0,n:n,pg:!0,pr:h._priority},a=h._overwriteProps.length;--a>-1;)i[h._overwriteProps[a]]=this._firstPT;(h._priority||h._onInitAllProps)&&(o=!0),(h._onDisable||h._onEnable)&&(this._notifyPluginsOfEnabled=!0)}else this._firstPT=i[n]=l={_next:this._firstPT,t:e,p:n,f:"function"==typeof e[n],n:n,pg:!1,pr:0},l.s=l.f?e[n.indexOf("set")||"function"!=typeof e["get"+n.substr(3)]?n:"get"+n.substr(3)]():parseFloat(e[n]),l.c="string"==typeof _&&"="===_.charAt(1)?parseInt(_.charAt(0)+"1",10)*Number(_.substr(2)):Number(_)-l.s||0;l&&l._next&&(l._next._prev=l)}return r&&this._kill(r,e)?this._initProps(e,i,s,r):this._overwrite>1&&this._firstPT&&s.length>1&&j(e,this,i,this._overwrite,s)?(this._kill(i,e),this._initProps(e,i,s,r)):o},r.render=function(t,e,i){var s,r,n,a,o=this._time,h=this._duration;if(t>=h)this._totalTime=this._time=h,this.ratio=this._ease._calcEnd?this._ease.getRatio(1):1,this._reversed||(s=!0,r="onComplete"),0===h&&(a=this._rawPrevTime,(0===t||0>a||a===l)&&a!==t&&(i=!0,a>l&&(r="onReverseComplete")),this._rawPrevTime=a=!e||t?t:l);else if(1e-7>t)this._totalTime=this._time=0,this.ratio=this._ease._calcEnd?this._ease.getRatio(0):0,(0!==o||0===h&&this._rawPrevTime>l)&&(r="onReverseComplete",s=this._reversed),0>t?(this._active=!1,0===h&&(this._rawPrevTime>=0&&(i=!0),this._rawPrevTime=a=!e||t?t:l)):this._initted||(i=!0);else if(this._totalTime=this._time=t,this._easeType){var _=t/h,u=this._easeType,p=this._easePower;(1===u||3===u&&_>=.5)&&(_=1-_),3===u&&(_*=2),1===p?_*=_:2===p?_*=_*_:3===p?_*=_*_*_:4===p&&(_*=_*_*_*_),this.ratio=1===u?1-_:2===u?_:.5>t/h?_/2:1-_/2}else this.ratio=this._ease.getRatio(t/h);if(this._time!==o||i){if(!this._initted){if(this._init(),!this._initted||this._gc)return;this._time&&!s?this.ratio=this._ease.getRatio(this._time/h):s&&this._ease._calcEnd&&(this.ratio=this._ease.getRatio(0===this._time?0:1))}for(this._active||!this._paused&&this._time!==o&&t>=0&&(this._active=!0),0===o&&(this._startAt&&(t>=0?this._startAt.render(t,e,i):r||(r="_dummyGS")),this.vars.onStart&&(0!==this._time||0===h)&&(e||this.vars.onStart.apply(this.vars.onStartScope||this,this.vars.onStartParams||v))),n=this._firstPT;n;)n.f?n.t[n.p](n.c*this.ratio+n.s):n.t[n.p]=n.c*this.ratio+n.s,n=n._next;this._onUpdate&&(0>t&&this._startAt&&this._startTime&&this._startAt.render(t,e,i),e||i&&0===this._time&&0===o||this._onUpdate.apply(this.vars.onUpdateScope||this,this.vars.onUpdateParams||v)),r&&(this._gc||(0>t&&this._startAt&&!this._onUpdate&&this._startTime&&this._startAt.render(t,e,i),s&&(this._timeline.autoRemoveChildren&&this._enabled(!1,!1),this._active=!1),!e&&this.vars[r]&&this.vars[r].apply(this.vars[r+"Scope"]||this,this.vars[r+"Params"]||v),0===h&&this._rawPrevTime===l&&a!==l&&(this._rawPrevTime=0)))}},r._kill=function(t,e){if("all"===t&&(t=null),null==t&&(null==e||e===this.target))return this._enabled(!1,!1);e="string"!=typeof e?e||this._targets||this.target:O.selector(e)||e;var i,s,r,n,a,o,h,l;if((p(e)||D(e))&&"number"!=typeof e[0])for(i=e.length;--i>-1;)this._kill(t,e[i])&&(o=!0);else{if(this._targets){for(i=this._targets.length;--i>-1;)if(e===this._targets[i]){a=this._propLookup[i]||{},this._overwrittenProps=this._overwrittenProps||[],s=this._overwrittenProps[i]=t?this._overwrittenProps[i]||{}:"all";break}}else{if(e!==this.target)return!1;a=this._propLookup,s=this._overwrittenProps=t?this._overwrittenProps||{}:"all"}if(a){h=t||a,l=t!==s&&"all"!==s&&t!==a&&("object"!=typeof t||!t._tempKill);for(r in h)(n=a[r])&&(n.pg&&n.t._kill(h)&&(o=!0),n.pg&&0!==n.t._overwriteProps.length||(n._prev?n._prev._next=n._next:n===this._firstPT&&(this._firstPT=n._next),n._next&&(n._next._prev=n._prev),n._next=n._prev=null),delete a[r]),l&&(s[r]=1);!this._firstPT&&this._initted&&this._enabled(!1,!1)}}return o},r.invalidate=function(){return this._notifyPluginsOfEnabled&&O._onPluginEvent("_onDisable",this),this._firstPT=null,this._overwrittenProps=null,this._onUpdate=null,this._startAt=null,this._initted=this._active=this._notifyPluginsOfEnabled=!1,this._propLookup=this._targets?{}:[],this},r._enabled=function(t,e){if(a||n.wake(),t&&this._gc){var i,s=this._targets;if(s)for(i=s.length;--i>-1;)this._siblings[i]=Y(s[i],this,!0);else this._siblings=Y(this.target,this,!0)}return R.prototype._enabled.call(this,t,e),this._notifyPluginsOfEnabled&&this._firstPT?O._onPluginEvent(t?"_onEnable":"_onDisable",this):!1}
vendor: 4,954 bytes, lines 52510-52552
52510,O.to=function(t,e,i){return new O(t,e,i)},O.from=function(t,e,i){return i.runBackwards=!0,i.immediateRender=0!=i.immediateRender,new O(t,e,i)},O.fromTo=function(t,e,i,s){return s.startAt=i,s.immediateRender=0!=s.immediateRender&&0!=i.immediateRender,new O(t,e,s)},O.delayedCall=function(t,e,i,s,r){return new O(e,0,{delay:t,onComplete:e,onCompleteParams:i,onCompleteScope:s,onReverseComplete:e,onReverseCompleteParams:i,onReverseCompleteScope:s,immediateRender:!1,useFrames:r,overwrite:0})},O.set=function(t,e){return new O(t,0,e)},O.getTweensOf=function(t,e){if(null==t)return[];t="string"!=typeof t?t:O.selector(t)||t;var i,s,r,n;if((p(t)||D(t))&&"number"!=typeof t[0]){for(i=t.length,s=[];--i>-1;)s=s.concat(O.getTweensOf(t[i],e));for(i=s.length;--i>-1;)for(n=s[i],r=i;--r>-1;)n===s[r]&&s.splice(i,1)}else for(s=Y(t).concat(),i=s.length;--i>-1;)(s[i]._gc||e&&!s[i].isActive())&&s.splice(i,1);return s},O.killTweensOf=O.killDelayedCallsTo=function(t,e,i){"object"==typeof e&&(i=e,e=!1);for(var s=O.getTweensOf(t,e),r=s.length;--r>-1;)s[r]._kill(i,t)};var q=d("plugins.TweenPlugin",function(t,e){this._overwriteProps=(t||"").split(","),this._propName=this._overwriteProps[0],this._priority=e||0,this._super=q.prototype},!0);if(r=q.prototype,q.version="1.10.1",q.API=2,r._firstPT=null,r._addTween=function(t,e,i,s,r,n){var a,o;return null!=s&&(a="number"==typeof s||"="!==s.charAt(1)?Number(s)-i:parseInt(s.charAt(0)+"1",10)*Number(s.substr(2)))?(this._firstPT=o={_next:this._firstPT,t:t,p:e,s:i,c:a,f:"function"==typeof t[e],n:r||e,r:n},o._next&&(o._next._prev=o),o):void 0},r.setRatio=function(t){for(var e,i=this._firstPT,s=1e-6;i;)e=i.c*t+i.s,i.r?e=0|e+(e>0?.5:-.5):s>e&&e>-s&&(e=0),i.f?i.t[i.p](e):i.t[i.p]=e,i=i._next},r._kill=function(t){var e,i=this._overwriteProps,s=this._firstPT;if(null!=t[this._propName])this._overwriteProps=[];else for(e=i.length;--e>-1;)null!=t[i[e]]&&i.splice(e,1);for(;s;)null!=t[s.n]&&(s._next&&(s._next._prev=s._prev),s._prev?(s._prev._next=s._next,s._prev=null):this._firstPT===s&&(this._firstPT=s._next)),s=s._next;return!1},r._roundProps=function(t,e){for(var i=this._firstPT;i;)(t[this._propName]||null!=i.n&&t[i.n.split(this._propName+"_").join("")])&&(i.r=e),i=i._next},O._onPluginEvent=function(t,e){var i,s,r,n,a,o=e._firstPT;if("_onInitAllProps"===t){for(;o;){for(a=o._next,s=r;s&&s.pr>o.pr;)s=s._next;(o._prev=s?s._prev:n)?o._prev._next=o:r=o,(o._next=s)?s._prev=o:n=o,o=a}o=e._firstPT=r}for(;o;)o.pg&&"function"==typeof o.t[t]&&o.t[t]()&&(i=!0),o=o._next;return i},q.activate=function(t){for(var e=t.length;--e>-1;)t[e].API===q.API&&(E[(new t[e])._propName]=t[e]);return!0},m.plugin=function(t){if(!(t&&t.propName&&t.init&&t.API))throw"illegal plugin definition.";var e,i=t.propName,s=t.priority||0,r=t.overwriteProps,n={init:"_onInitTween",set:"setRatio",kill:"_kill",round:"_roundProps",initAll:"_onInitAllProps"},a=d("plugins."+i.charAt(0).toUpperCase()+i.substr(1)+"Plugin",function(){q.call(this,i,s),this._overwriteProps=r||[]},t.global===!0),o=a.prototype=new q(i);o.constructor=a,a.API=t.API;for(e in n)"function"==typeof t[e]&&(o[n[e]]=t[e]);return a.version=t.version,q.activate([a]),a},i=t._gsQueue){for(s=0;i.length>s;s++)i[s]();for(r in f)f[r].func||t.console.log("GSAP encountered missing dependency: com.greensock."+r)}a=!1}}(window); 52511/** 52512@file 52513JS for the WIS? (and what-is-dianetics.*) pages. 52514*/ 52515 52516// don't clobber any settings in individual HTML files 52517if (!window.globalSlideshowOptionsOverride) { 52518 // always set circular autosliding 52519 globalSlideshowOptionsOverride = { 52520 circularClone: true, 52521 } 52522} 52523 52524/* 52525 * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/ 52526 * 52527 * Uses the built in easing capabilities added In jQuery 1.1 52528 * to offer multiple easing options 52529 * 52530 * TERMS OF USE - jQuery Easing 52531 * 52532 * Open source under the BSD License. 52533 * 52534 * Copyright © 2008 George McGinley Smith 52535 * All rights reserved. 52536 * 52537 * Redistribution and use in source and binary forms, with or without modification, 52538 * are permitted provided that the following conditions are met: 52539 * 52540 * Redistributions of source code must retain the above copyright notice, this list of 52541 * conditions and the following disclaimer. 52542 * Redistributions in binary form must reproduce the above copyright notice, this list 52543 * of conditions and the following disclaimer in the documentation and/or other materials 52544 * provided with the distribution. 52545 * 52546 * Neither the name of the author nor the names of contributors may be used to endorse 52547 * or promote products derived from this software without specific prior written permission. 52548 * 52549 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 52550 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 52551 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 52552 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
vendor: 5,091 bytes, lines 52553-52698
52553 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 52554 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 52555 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 52556 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 52557 * OF THE POSSIBILITY OF SUCH DAMAGE. 52558 * 52559*/ 52560 52561// t: current time, b: begInnIng value, c: change In value, d: duration 52562jQuery.easing['jswing'] = jQuery.easing['swing']; 52563 52564jQuery.extend( jQuery.easing, 52565{ 52566 def: 'easeOutQuad', 52567 swing: function (x, t, b, c, d) { 52568 //alert(jQuery.easing.default); 52569 return jQuery.easing[jQuery.easing.def](x, t, b, c, d); 52570 }, 52571 easeInQuad: function (x, t, b, c, d) { 52572 return c*(t/=d)*t + b; 52573 }, 52574 easeOutQuad: function (x, t, b, c, d) { 52575 return -c *(t/=d)*(t-2) + b; 52576 }, 52577 easeInOutQuad: function (x, t, b, c, d) { 52578 if ((t/=d/2) < 1) return c/2*t*t + b; 52579 return -c/2 * ((--t)*(t-2) - 1) + b; 52580 }, 52581 easeInCubic: function (x, t, b, c, d) { 52582 return c*(t/=d)*t*t + b; 52583 }, 52584 easeOutCubic: function (x, t, b, c, d) { 52585 return c*((t=t/d-1)*t*t + 1) + b; 52586 }, 52587 easeInOutCubic: function (x, t, b, c, d) { 52588 if ((t/=d/2) < 1) return c/2*t*t*t + b; 52589 return c/2*((t-=2)*t*t + 2) + b; 52590 }, 52591 easeInQuart: function (x, t, b, c, d) { 52592 return c*(t/=d)*t*t*t + b; 52593 }, 52594 easeOutQuart: function (x, t, b, c, d) { 52595 return -c * ((t=t/d-1)*t*t*t - 1) + b; 52596 }, 52597 easeInOutQuart: function (x, t, b, c, d) { 52598 if ((t/=d/2) < 1) return c/2*t*t*t*t + b; 52599 return -c/2 * ((t-=2)*t*t*t - 2) + b; 52600 }, 52601 easeInQuint: function (x, t, b, c, d) { 52602 return c*(t/=d)*t*t*t*t + b; 52603 }, 52604 easeOutQuint: function (x, t, b, c, d) { 52605 return c*((t=t/d-1)*t*t*t*t + 1) + b; 52606 }, 52607 easeInOutQuint: function (x, t, b, c, d) { 52608 if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b; 52609 return c/2*((t-=2)*t*t*t*t + 2) + b; 52610 }, 52611 easeInSine: function (x, t, b, c, d) { 52612 return -c * Math.cos(t/d * (Math.PI/2)) + c + b; 52613 }, 52614 easeOutSine: function (x, t, b, c, d) { 52615 return c * Math.sin(t/d * (Math.PI/2)) + b; 52616 }, 52617 easeInOutSine: function (x, t, b, c, d) { 52618 return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b; 52619 }, 52620 easeInExpo: function (x, t, b, c, d) { 52621 return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b; 52622 }, 52623 easeOutExpo: function (x, t, b, c, d) { 52624 return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; 52625 }, 52626 easeInOutExpo: function (x, t, b, c, d) { 52627 if (t==0) return b; 52628 if (t==d) return b+c; 52629 if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b; 52630 return c/2 * (-Math.pow(2, -10 * --t) + 2) + b; 52631 }, 52632 easeInCirc: function (x, t, b, c, d) { 52633 return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b; 52634 }, 52635 easeOutCirc: function (x, t, b, c, d) { 52636 return c * Math.sqrt(1 - (t=t/d-1)*t) + b; 52637 }, 52638 easeInOutCirc: function (x, t, b, c, d) { 52639 if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b; 52640 return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b; 52641 }, 52642 easeInElastic: function (x, t, b, c, d) { 52643 var s=1.70158;var p=0;var a=c; 52644 if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; 52645 if (a < Math.abs(c)) { a=c; var s=p/4; } 52646 else var s = p/(2*Math.PI) * Math.asin (c/a); 52647 return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; 52648 }, 52649 easeOutElastic: function (x, t, b, c, d) { 52650 var s=1.70158;var p=0;var a=c; 52651 if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; 52652 if (a < Math.abs(c)) { a=c; var s=p/4; } 52653 else var s = p/(2*Math.PI) * Math.asin (c/a); 52654 return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b; 52655 }, 52656 easeInOutElastic: function (x, t, b, c, d) { 52657 var s=1.70158;var p=0;var a=c; 52658 if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5); 52659 if (a < Math.abs(c)) { a=c; var s=p/4; } 52660 else var s = p/(2*Math.PI) * Math.asin (c/a); 52661 if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; 52662 return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b; 52663 }, 52664 easeInBack: function (x, t, b, c, d, s) { 52665 if (s == undefined) s = 1.70158; 52666 return c*(t/=d)*t*((s+1)*t - s) + b; 52667 }, 52668 easeOutBack: function (x, t, b, c, d, s) { 52669 if (s == undefined) s = 1.70158; 52670 return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; 52671 }, 52672 easeInOutBack: function (x, t, b, c, d, s) { 52673 if (s == undefined) s = 1.70158; 52674 if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b; 52675 return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b; 52676 }, 52677 easeInBounce: function (x, t, b, c, d) { 52678 return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b; 52679 }, 52680 easeOutBounce: function (x, t, b, c, d) { 52681 if ((t/=d) < (1/2.75)) { 52682 return c*(7.5625*t*t) + b; 52683 } else if (t < (2/2.75)) { 52684 return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b; 52685 } else if (t < (2.5/2.75)) { 52686 return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b; 52687 } else { 52688 return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b; 52689 } 52690 }, 52691 easeInOutBounce: function (x, t, b, c, d) { 52692 if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b; 52693 return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b; 52694 } 52695}); 52696 52697/* 52698 *
52699 * TERMS OF USE - EASING EQUATIONS 52700 * 52701 * Open source under the BSD License. 52702 * 52703 * Copyright © 2001 Robert Penner 52704 * All rights reserved. 52705 * 52706 * Redistribution and use in source and binary forms, with or without modification, 52707 * are permitted provided that the following conditions are met: 52708 * 52709 * Redistributions of source code must retain the above copyright notice, this list of 52710 * conditions and the following disclaimer. 52711 * Redistributions in binary form must reproduce the above copyright notice, this list 52712 * of conditions and the following disclaimer in the documentation and/or other materials 52713 * provided with the distribution. 52714 * 52715 * Neither the name of the author nor the names of contributors may be used to endorse 52716 * or promote products derived from this software without specific prior written permission. 52717 * 52718 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 52719 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 52720 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 52721 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 52722 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 52723 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 52724 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 52725 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 52726 * OF THE POSSIBILITY OF SUCH DAMAGE. 52727 * 52728 */
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.